1.裝飾模式簡介
裝飾模式介紹
裝飾模式是結(jié)構(gòu)型設(shè)計模式之一,不必改變類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能,是繼承的替代方案之一。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象。
定義
動態(tài)地給一個對象添加一些額外的職責(zé),就增加功能來說,裝飾模式比生成子類更為靈活。
裝飾模式結(jié)構(gòu)圖
- Component:抽象組件,給對象動態(tài)的添加職責(zé)。
- ConcreteComponent:組件具體實現(xiàn)類。
- Decorator:抽象裝飾者,繼承Component,從外類來拓展Component類的功能,但對于Component來說無需知道Decorator的存在。
- ConcreteDecorator:裝飾者具體實現(xiàn)類。
2.裝飾模式的簡單實現(xiàn)
裝飾模式在現(xiàn)實生活中有很多例子,比如給一個人穿上各種衣服,給一幅畫涂色上框等等,這次我要舉得例子有些不同,舉一個武俠修煉武功的例子:楊過本身就會全真劍法,有兩位武學(xué)前輩洪七公和歐陽鋒分別傳授楊過打狗棒法和蛤蟆功,這樣楊過除了會全真劍法還會打狗棒法和蛤蟆功。
抽象組件(Component)
作為武俠肯定要會使用武功的,我們先定義一個武俠的抽象類,里面有使用武功的抽象方法:
public abstract class Swordsman {
/**
* Swordsman武俠有使用武功的抽象方法
*/
public abstract void attackMagic();
}
組件具體實現(xiàn)類(ConcreteComponent)
被裝飾的具體對象,在這里就是被教授武學(xué)的具體的武俠,他就是楊過,楊過作為武俠當然也會武學(xué),雖然不怎么厲害:
public class YangGuo extends Swordsman{
@Override
public void attackMagic() {
//楊過初始的武學(xué)是全真劍法
System.out.println("楊過使用全真劍法");
}
}
抽象裝飾者(Decorator)
抽象裝飾者保持了一個對抽象組件的引用,方便調(diào)用被裝飾對象中的方法。在這個例子中就是武學(xué)前輩要持有武俠的引用,方便教授他武學(xué)并“融會貫通”:
public abstract class Master extends Swordsman{
private Swordsman mSwordsman;
public Master(Swordsman mSwordsman){
this.mSwordsman=mSwordsman;
}
@Override
public void attackMagic() {
mSwordsman.attackMagic();
}
}
裝飾者具體實現(xiàn)類(ConcreteDecorator)
這個例子中用兩個裝飾者具體實現(xiàn)類,分別是洪七公和歐陽鋒,他們負責(zé)來傳授楊過新的武功:
public class HongQiGong extends Master {
public HongQiGong(Swordsman mSwordsman) {
super(mSwordsman);
}
public void teachAttackMagic(){
System.out.println("洪七公教授打狗棒法");
System.out.println("楊過使用打狗棒法");
}
@Override
public void attackMagic() {
super.attackMagic();
teachAttackMagic();
}
}
public class OuYangFeng extends Master {
public OuYangFeng(Swordsman mSwordsman) {
super(mSwordsman);
}
public void teachAttackMagic(){
System.out.println("歐陽鋒教授蛤蟆功");
System.out.println("楊過使用蛤蟆功");
}
@Override
public void attackMagic() {
super.attackMagic();
teachAttackMagic();
}
}
客戶端調(diào)用
經(jīng)過洪七公和歐陽鋒的教導(dǎo),楊過除了初始武學(xué)全真劍法又學(xué)會了打狗棒法和蛤蟆功:
public class Client {
public static void main(String[] args){
//創(chuàng)建楊過
YangGuo mYangGuo=new YangGuo();
//洪七公教授楊過打狗棒法,楊過會了打狗棒法
HongQiGong mHongQiGong=new HongQiGong(mYangGuo);
mHongQiGong.attackMagic();
//歐陽鋒教授楊過蛤蟆功,楊過學(xué)會了蛤蟆功
OuYangFeng mOuYangFeng=new OuYangFeng(mYangGuo);
mOuYangFeng.attackMagic();
}
}
3.裝飾模式的優(yōu)缺點和使用場景
優(yōu)點
- 通過組合而非繼承的方式,動態(tài)的來擴展一個對象的功能,在運行時選擇不同的裝飾器,從而實現(xiàn)不同的行為。
- 有效避免了使用繼承的方式擴展對象功能而帶來的靈活性差,子類無限制擴張的問題。
- 具體組件類與具體裝飾類可以獨立變化,用戶可以根據(jù)需要增加新的具體組件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合“開閉原則”。
缺點
- 裝飾鏈不能過長,否則會影響效率。
- 因為所有對象都是繼承于Component,所以如果Component內(nèi)部結(jié)構(gòu)發(fā)生改變,則不可避免地影響所有子類(裝飾者和被裝飾者),如果基類改變,勢必影響對象的內(nèi)部。
- 比繼承更加靈活機動的特性,也同時意味著裝飾模式比繼承更加易于出錯,排錯也很困難,對于多次裝飾的對象,調(diào)試時尋找錯誤可能需要逐級排查,較為煩瑣,所以只在必要的時候使用裝飾者模式。
使用場景
- 在不影響其他對象的情況下,以動態(tài)、透明的方式給單個對象添加職責(zé)。
- 需要動態(tài)地給一個對象增加功能,這些功能可以動態(tài)的撤銷。
- 當不能采用繼承的方式對系統(tǒng)進行擴充或者采用繼承不利于系統(tǒng)擴展和維護時。
4.裝飾模式和代理模式
在上一篇文章設(shè)計模式之代理模式中我們講到了代理模式,你會覺得代理模式和裝飾模式有點像,都是持有了被代理或者被裝飾對象的引用。它們兩個最大的不同就是裝飾模式對引用的對象增加了功能,而代理模式只是對引用對象進行了控制卻沒有對引用對象本身增加功能。