詳解Java如何通過裝飾器模式擴(kuò)展系統(tǒng)功能
盡管目前房?jī)r(jià)依舊很高,買房的人依舊很多。如果大家買的是毛坯房,無疑還有一項(xiàng)艱巨的任務(wù)要面對(duì),那就是裝修。對(duì)新房進(jìn)行裝修并沒有改變房屋用于居住的本質(zhì),但它可以讓房子變得更漂亮、更溫馨、更實(shí)用、更能滿足居家的需求。在軟件設(shè)計(jì)中,我們也有一種類似新房裝修的技術(shù)可以對(duì)已有對(duì)象(新房)的功能進(jìn)行擴(kuò)展(裝修),以獲得更加符合用戶需求的對(duì)象,使得對(duì)象具有更加強(qiáng)大的功能。這種技術(shù)對(duì)應(yīng)于一種被稱之為裝飾模式的設(shè)計(jì)模式,本章將介紹用于擴(kuò)展系統(tǒng)功能的裝飾模式。
裝飾器模式概述
裝飾模式可以在不改變一個(gè)對(duì)象本身功能的基礎(chǔ)上給對(duì)象增加額外的新行為,在現(xiàn)實(shí)生活中,這種情況也到處存在,例如一張照片,我們可以不改變照片本身,給它增加一個(gè)相框,使得它具有防潮的功能,而且用戶可以根據(jù)需要給它增加不同類型的相框,甚至可以在一個(gè)小相框的外面再套一個(gè)大相框。
裝飾模式是一種用于替代繼承的技術(shù),它通過一種無須定義子類的方式來給對(duì)象動(dòng)態(tài)增加職責(zé),使用對(duì)象之間的關(guān)聯(lián)關(guān)系取代類之間的繼承關(guān)系。在裝飾模式中引入了裝飾類,在裝飾類中既可以調(diào)用待裝飾的原有類的方法,還可以增加新的方法,以擴(kuò)充原有類的功能。
裝飾模式結(jié)構(gòu)圖:

- Component(抽象構(gòu)件):它是具體構(gòu)件和抽象裝飾類的共同父類,聲明了在具體構(gòu)件中實(shí)現(xiàn)的業(yè)務(wù)方法,它的引入可以使客戶端以一致的方式處理未被裝飾的對(duì)象以及裝飾之后的對(duì)象,實(shí)現(xiàn)客戶端的透明操作。
- ConcreteComponent(具體構(gòu)件):它是抽象構(gòu)件類的子類,用于定義具體的構(gòu)件對(duì)象,實(shí)現(xiàn)了在抽象構(gòu)件中聲明的方法,裝飾器可以給它增加額外的職責(zé)(方法)。
- Decorator(抽象裝飾類):它也是抽象構(gòu)件類的子類,用于給具體構(gòu)件增加職責(zé),但是具體職責(zé)在其子類中實(shí)現(xiàn)。它維護(hù)一個(gè)指向抽象構(gòu)件對(duì)象的引用,通過該引用可以調(diào)用裝飾之前構(gòu)件對(duì)象的方法,并通過其子類擴(kuò)展該方法,以達(dá)到裝飾的目的。
- ConcreteDecorator(具體裝飾類):它是抽象裝飾類的子類,負(fù)責(zé)向構(gòu)件添加新的職責(zé)。每一個(gè)具體裝飾類都定義了一些新的行為,它可以調(diào)用在抽象裝飾類中定義的方法,并可以增加新的方法用以擴(kuò)充對(duì)象的行為。
由于具體構(gòu)件類和裝飾類都實(shí)現(xiàn)了相同的抽象構(gòu)件接口,因此裝飾模式以對(duì)客戶透明的方式動(dòng)態(tài)地給一個(gè)對(duì)象附加上更多的責(zé)任,換言之,客戶端并不會(huì)覺得對(duì)象在裝飾前和裝飾后有什么不同。裝飾模式可以在不需要?jiǎng)?chuàng)造更多子類的情況下,將對(duì)象的功能加以擴(kuò)展。
簡(jiǎn)單案例
場(chǎng)景:天氣太熱了,喝點(diǎn)兒冰水解解暑;加點(diǎn)兒檸檬片,讓果汁好喝點(diǎn)兒。
先定義一個(gè)喝水的接口
public interface Drink {
/**
* 喝水
*/
void drink();
}
寫一個(gè)接口的實(shí)現(xiàn)
public class DrinkWater implements Drink {
@Override
public void drink() {
System.out.println("喝水");
}
}一個(gè)簡(jiǎn)單的裝飾器
public class DrinkDecorator implements Drink {
private final Drink drink;
public DrinkDecorator(Drink drink) {
this.drink = drink;
}
@Override
public void drink() {
System.out.println("先加點(diǎn)兒檸檬片");
drink.drink();
}
}測(cè)試
public class DrinkMain {
public static void main(String[] args) {
Drink drink = new DrinkWater();
drink = new DrinkDecorator(drink);
drink.drink();
}
}運(yùn)行結(jié)果
先加點(diǎn)兒檸檬片
喝水
一個(gè)簡(jiǎn)單的裝飾器模式例子就寫完了,這里只是先了解一下裝飾器模式。
案例場(chǎng)景
大家應(yīng)該都喝過奶茶吧,我經(jīng)常喝的原味奶茶,里面可以添加配料,每增加一種配料,該奶茶的價(jià)格就會(huì)增加,要求計(jì)算一種奶茶的價(jià)格。
下面就可以使用裝飾器模式來模擬這種場(chǎng)景。
定義抽象構(gòu)件類
/**
* 奶茶
*/
public interface MilkyTea {
double cost();
}
定義具體構(gòu)件類
/**
* 珍珠奶茶
*/
public class PearlMilkyTea implements MilkyTea{
@Override
public double cost() {
return 13.0;
}
}定義抽象裝飾類
public abstract class MilkyTeaDecorator implements MilkyTea{
private MilkyTea milkyTea;
public MilkyTeaDecorator(MilkyTea milkyTea){
this.milkyTea = milkyTea;
}
@Override
public double cost() {
return milkyTea.cost();
}
}定義具體裝飾類
/**
* 椰果奶茶
*/
public class CoconutDecorator extends MilkyTeaDecorator{
public CoconutDecorator(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
public double cost() {
System.out.println("添加椰果");
return super.cost() + 2.0;
}
}
/**
* 布丁奶茶
*/
public class PuddingDecorator extends MilkyTeaDecorator{
public PuddingDecorator(MilkyTea milkyTea) {
super(milkyTea);
}
@Override
public double cost() {
System.out.println("添加布丁");
return super.cost() + 1.5;
}
}測(cè)試代碼
public class Client {
public static void main(String[] args) {
MilkyTea milkyTea = new PearlMilkyTea();
milkyTea = new CoconutDecorator(milkyTea);
milkyTea = new PuddingDecorator(milkyTea);
System.out.println(milkyTea.cost());
}
}運(yùn)行結(jié)果
添加布丁
添加椰果
16.5
在客戶端代碼中,我們先定義了一個(gè)MilkyTea類型的具體構(gòu)件對(duì)象milkyTea,然后將milkyTea作為構(gòu)造函數(shù)的參數(shù)注入到具體裝飾類CoconutDecorator中,得到一個(gè)裝飾之后對(duì)象milkyTea,再將裝飾了一次之后的milkyTea對(duì)象注入另一個(gè)裝飾類PuddingDecorator中實(shí)現(xiàn)第二次裝飾,得到一個(gè)經(jīng)過兩次裝飾的對(duì)象milkyTea,再調(diào)用milkyTea的cost()方法即可得到一杯既加了布丁又加了椰果的珍珠奶茶價(jià)格。
裝飾器模式優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 裝飾類和被裝飾類可以獨(dú)立發(fā)展,不會(huì)相互耦合。
- 相比于繼承,更加的輕便、靈活。
- 可以動(dòng)態(tài)擴(kuò)展一個(gè)實(shí)現(xiàn)類的功能,不必修改原本代碼
缺點(diǎn)
- 會(huì)產(chǎn)生很多的裝飾類,增加了系統(tǒng)的復(fù)雜性。
- 這種比繼承更加靈活機(jī)動(dòng)的特性,也同時(shí)意味著裝飾模式比繼承易于出錯(cuò),排錯(cuò)也很困難,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查,較為繁瑣。
裝飾器模式適用場(chǎng)景
在不影響其他對(duì)象的情況下,以動(dòng)態(tài)、透明的方式給單個(gè)對(duì)象添加職責(zé)。
當(dāng)不能采用繼承的方式對(duì)系統(tǒng)進(jìn)行擴(kuò)展或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)可以使用裝飾模式。不能采用繼承的情況主要有兩類:第一類是系統(tǒng)中存在大量獨(dú)立的擴(kuò)展,為支持每一種擴(kuò)展或者擴(kuò)展之間的組合將產(chǎn)生大量的子類,使得子類數(shù)目呈爆炸性增長(zhǎng);第二類是因?yàn)轭愐讯x為不能被繼承(如Java語言中的final類)。
總結(jié)
裝飾模式降低了系統(tǒng)的耦合度,可以動(dòng)態(tài)增加或刪除對(duì)象的職責(zé),并使得需要裝飾的具體構(gòu)件類和具體裝飾類可以獨(dú)立變化,以便增加新的具體構(gòu)件類和具體裝飾類。在軟件開發(fā)中,裝飾模式應(yīng)用較為廣泛,例如在JavaIO中的輸入流和輸出流的設(shè)計(jì)、javax.swing包中一些圖形界面構(gòu)件功能的增強(qiáng)等地方都運(yùn)用了裝飾模式。
到此這篇關(guān)于詳解Java如何通過裝飾器模式擴(kuò)展系統(tǒng)功能的文章就介紹到這了,更多相關(guān)Java裝飾器模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC文件上傳原理及實(shí)現(xiàn)過程解析
這篇文章主要介紹了SpringMVC文件上傳原理及實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
淺析java中String類型中“==”與“equal”的區(qū)別
這篇文章主要介紹了淺析java中String類型中“==”與“equal”的區(qū)別,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Java實(shí)現(xiàn)網(wǎng)絡(luò)文件下載以及下載到指定目錄
在Spring框架中,StreamUtils和FileCopyUtils兩個(gè)工具類提供了方便的文件下載功能,它們都屬于org.springframework.util包,可以通過簡(jiǎn)單的方法調(diào)用實(shí)現(xiàn)文件流的復(fù)制和下載,這些工具類支持多種參數(shù)傳遞,涵蓋了文件下載的多種場(chǎng)景2024-09-09
Java 把json對(duì)象轉(zhuǎn)成map鍵值對(duì)的方法
這篇文章主要介紹了java 把json對(duì)象中轉(zhuǎn)成map鍵值對(duì)的方法,本文的目的是把json串轉(zhuǎn)成map鍵值對(duì)存儲(chǔ),而且只存儲(chǔ)葉節(jié)點(diǎn)的數(shù)據(jù) 。需要的朋友可以參考下2018-04-04
springboot配置mysql數(shù)據(jù)庫spring.datasource.url報(bào)錯(cuò)的解決
這篇文章主要介紹了springboot配置mysql數(shù)據(jù)庫spring.datasource.url報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Spring中事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API詳解
這篇文章主要介紹了Spring中事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API詳解,事務(wù)管理是指對(duì)事務(wù)進(jìn)行管理和控制,以確保事務(wù)的正確性和完整性,事務(wù)管理的作用是保證數(shù)據(jù)庫的數(shù)據(jù)操作的一致性和可靠性,需要的朋友可以參考下2023-08-08
MyBatis-Plus中如何配置加密功能(使用AES算法)
本文將詳細(xì)介紹如何實(shí)現(xiàn) MyBatis-Plus 中的配置加密功能,并給出相應(yīng)的代碼示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03

