真誠才是人生最高的品德膜毁。 — 喬叟
寫在前面
裝飾模式在不必改變類文件和使用繼承的情況下,可以動態(tài)的拓展一個對象的功能愤钾,是繼承的替代方案之一瘟滨。它通過創(chuàng)建包裝對象也就是裝飾者包裹真實的對象。
裝飾模式的定義:動態(tài)的給一個對象添加一些額外的功能能颁,就增加功能來說杂瘸,裝飾模式比生成子類更加靈活。
抽象組件類(Component):可以是接口或者抽象類伙菊,被裝飾的最原始的對象败玉。
組件具體實現(xiàn)類(ConcreteComponent):Component的具體實現(xiàn)類,被裝飾的具體對象镜硕。
抽象裝飾類(Decorator):從外類來拓展Component類的功能运翼,但對于Component類來說無需知道Decorator的存在。在它的屬性中必然有一個私有變量指向Component抽象組件兴枯。
裝飾具體實現(xiàn)類(ConcreteDecorator):具體裝飾者血淌。
如何裝飾
下面通過一個例子來認識裝飾模式:
現(xiàn)在有一張畫紙,我畫了一頭奶牛财剖,但是因為我是色盲悠夯,所以就讓張三來幫忙給畫上色,上完色發(fā)現(xiàn)這副畫真的太好看了峰伙,倒不如裱起來掛到墻上欣賞疗疟,但是我不會该默,就讓李四來給這幅畫裝上畫框瞳氓。這里我是被裝飾者,張三和李四是裝飾者。
1.抽象組件類
我匣摘,張三和李四都要操作這幅畫店诗,我們都有操作的能力,所以要創(chuàng)建一個抽象類音榜,并聲明一個操作的方法庞瘸。
/**
* 抽象組件類
*/
public abstract class Component {
abstract void todo();
}
2.組件具體實現(xiàn)類
我只有畫的能力,上色和安裝畫框都是在我作畫的基礎(chǔ)上進行的赠叼,我要先作畫擦囊,因此我是被裝飾者。
/**
* 組件具體實現(xiàn)類
*/
public class ConcreteComponent extends Component {
@Override
public void todo() {
Log.d("ConcreteComponent", "我創(chuàng)作了一幅畫W彀臁K渤 !");
}
}
3.抽象裝飾類
抽象裝飾類要持有對一個抽象組件的引用涧郊,方便調(diào)用被裝飾對象中的方法贯被,抽象裝飾類只是一個聲明。
/**
* 抽象裝飾類
*/
public abstract class Decoratorextends Component {
private Component mComponent;
public Decorator(Component component) {
mComponent = component;
}
@Override
public void todo() {
mComponent.todo();
}
}
4.裝飾具體實現(xiàn)類
當(dāng)我做好了畫妆艘,張三和李四就要分別對畫進行上色和裝框了彤灶,張三和李四就是具體的裝飾類。
/**
* 裝飾具體實現(xiàn)類
*/
public class ZhangSanDecorator extends Decorator {
public ZhangSanDecorator(Component component) {
super(component);
}
@Override
public void todo() {
super.todo();
shangSe();
}
public void shangSe(){
Log.d("張三", "給畫上色E;仙隆!");
}
}
/**
* 裝飾具體實現(xiàn)類
*/
public class LiSiDecorator extends Decorator {
public LiSiDecorator(Component component) {
super(component);
}
@Override
public void todo() {
super.todo();
zhuangKuang();
}
public void zhuangKuang() {
Log.d("李四", "給畫裝框V煳帧0巍!");
}
}
5.開始裝飾
首先我要作畫逗物,張三和李四分別對我的畫進行裝飾搬卒,所以要將我作為參數(shù)傳給張三和李四,這樣他們就能在我作畫的基礎(chǔ)上對畫進行裝飾了翎卓。
/**
* 客戶端
*/
public class Client {
public Client() {
// 創(chuàng)建我對象契邀,調(diào)用todo()方法作畫。
ConcreteComponent me = new ConcreteComponent();
me.todo();
// 創(chuàng)建張三對象失暴,調(diào)用todo()方法給畫上色坯门。
ZhangSanDecorator zhangSan = new ZhangSanDecorator(me);
zhangSan.todo();
// 創(chuàng)建李四對象,調(diào)用todo()方法給畫裝框
LiSiDecorator liSi = new LiSiDecorator(me);
liSi.todo();
}
}
總結(jié)
裝飾模式的使用場景:
- 在不影響其他對象的情況下逗扒,以動態(tài)古戴,透明的方式為單個對象添加職責(zé)。
- 需要動態(tài)的為一個對象增加功能矩肩,這些功能可以動態(tài)的撤銷现恼。
- 當(dāng)不能采用繼承的方式對系統(tǒng)進行擴充或者采用繼承的方式不利于系統(tǒng)擴展和維護時。
裝飾模式的優(yōu)點:
- 通過組合而非繼承的方式,動態(tài)的擴展對象的功能叉袍,在運行時選擇不同的裝飾器始锚,從而實現(xiàn)不同的行為。
- 有效避免了使用繼承的方式擴展對象功能而帶來的靈活性差喳逛,子類無限制擴張的問題瞧捌。
- 具體組件類和具體裝飾類可以獨立變化,用戶可以根據(jù)需要增加新的具體組件類和具體裝飾類润文,在使用時再對其進行組合姐呐,原有代碼無需變化,符合“開發(fā)封閉原則”典蝌。
裝飾模式的缺點:
- 因為所有對象均繼承于Component皮钠,所以如果Component內(nèi)部結(jié)構(gòu)發(fā)生改變,則不可避免的影響所有子類赠法,就是會影響裝飾者和被裝飾者麦轰。
- 比繼承更加靈活機動的特性,也同時意味者裝飾模式比繼承更加容易出錯砖织,排錯也很困難款侵。對于多次裝飾的對象,調(diào)試時尋找錯誤可能需要逐級排查侧纯,較為繁瑣新锈。所以只在必要的時候使用裝飾模式。
- 裝飾層數(shù)不能過多眶熬,否則會影響效率妹笆。