1.裝飾模式簡(jiǎn)介
裝飾模式介紹
裝飾模式是結(jié)構(gòu)型設(shè)計(jì)模式之一集乔,不必改變類文件和使用繼承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能笨农,是繼承的替代方案之一。它是通過創(chuàng)建一個(gè)包裝對(duì)象帖渠,也就是裝飾來包裹真實(shí)的對(duì)象谒亦。
定義
動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé),就增加功能來說空郊,裝飾模式比生成子類更為靈活份招。
裝飾模式結(jié)構(gòu)圖
- Component:抽象組件,給對(duì)象動(dòng)態(tài)的添加職責(zé)狞甚。
- ConcreteComponent:組件具體實(shí)現(xiàn)類锁摔。
- Decorator:抽象裝飾者,繼承Component哼审,從外類來拓展Component類的功能谐腰,但對(duì)于Component來說無需知道Decorator的存在。
- ConcreteDecorator:裝飾者具體實(shí)現(xiàn)類涩盾。
2.裝飾模式的簡(jiǎn)單實(shí)現(xiàn)
裝飾模式在現(xiàn)實(shí)生活中有很多例子十气,比如給一個(gè)人穿上各種衣服,給一幅畫涂色上框等等春霍,這次我要舉得例子有些不同砸西,舉一個(gè)武俠修煉武功的例子:楊過本身就會(huì)全真劍法,有兩位武學(xué)前輩洪七公和歐陽(yáng)鋒分別傳授楊過打狗棒法和蛤蟆功址儒,這樣楊過除了會(huì)全真劍法還會(huì)打狗棒法和蛤蟆功芹枷。
抽象組件(Component)
作為武俠肯定要會(huì)使用武功的,我們先定義一個(gè)武俠的抽象類莲趣,里面有使用武功的抽象方法:
public abstract class Swordsman {
/**
* Swordsman武俠有使用武功的抽象方法
*/
public abstract void attackMagic();
}
組件具體實(shí)現(xiàn)類(ConcreteComponent)
被裝飾的具體對(duì)象鸳慈,在這里就是被教授武學(xué)的具體的武俠,他就是楊過喧伞,楊過作為武俠當(dāng)然也會(huì)武學(xué)蝶涩,雖然不怎么厲害:
public class YangGuo extends Swordsman{
@Override
public void attackMagic() {
//楊過初始的武學(xué)是全真劍法
System.out.println("楊過使用全真劍法");
}
}
抽象裝飾者(Decorator)
抽象裝飾者保持了一個(gè)對(duì)抽象組件的引用理朋,方便調(diào)用被裝飾對(duì)象中的方法。在這個(gè)例子中就是武學(xué)前輩要持有武俠的引用绿聘,方便教授他武學(xué)并“融會(huì)貫通”:
public abstract class Master extends Swordsman{
private Swordsman mSwordsman;
public Master(Swordsman mSwordsman){
this.mSwordsman=mSwordsman;
}
@Override
public void attackMagic() {
mSwordsman.attackMagic();
}
}
裝飾者具體實(shí)現(xiàn)類(ConcreteDecorator)
這個(gè)例子中用兩個(gè)裝飾者具體實(shí)現(xiàn)類嗽上,分別是洪七公和歐陽(yáng)鋒,他們負(fù)責(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("歐陽(yáng)鋒教授蛤蟆功");
System.out.println("楊過使用蛤蟆功");
}
@Override
public void attackMagic() {
super.attackMagic();
teachAttackMagic();
}
}
客戶端調(diào)用
經(jīng)過洪七公和歐陽(yáng)鋒的教導(dǎo)熄攘,楊過除了初始武學(xué)全真劍法又學(xué)會(huì)了打狗棒法和蛤蟆功:
public class Client {
public static void main(String[] args){
//創(chuàng)建楊過
YangGuo mYangGuo=new YangGuo();
//洪七公教授楊過打狗棒法兽愤,楊過會(huì)了打狗棒法
HongQiGong mHongQiGong=new HongQiGong(mYangGuo);
mHongQiGong.attackMagic();
//歐陽(yáng)鋒教授楊過蛤蟆功,楊過學(xué)會(huì)了蛤蟆功
OuYangFeng mOuYangFeng=new OuYangFeng(mYangGuo);
mOuYangFeng.attackMagic();
}
}
3.裝飾模式的優(yōu)缺點(diǎn)和使用場(chǎng)景
優(yōu)點(diǎn)
- 通過組合而非繼承的方式挪圾,動(dòng)態(tài)的來擴(kuò)展一個(gè)對(duì)象的功能浅萧,在運(yùn)行時(shí)選擇不同的裝飾器,從而實(shí)現(xiàn)不同的行為哲思。
- 有效避免了使用繼承的方式擴(kuò)展對(duì)象功能而帶來的靈活性差洼畅,子類無限制擴(kuò)張的問題。
- 具體組件類與具體裝飾類可以獨(dú)立變化棚赔,用戶可以根據(jù)需要增加新的具體組件類和具體裝飾類帝簇,在使用時(shí)再對(duì)其進(jìn)行組合,原有代碼無須改變靠益,符合“開閉原則”丧肴。
缺點(diǎn)
- 裝飾鏈不能過長(zhǎng),否則會(huì)影響效率胧后。
- 因?yàn)樗袑?duì)象都是繼承于Component,所以如果Component內(nèi)部結(jié)構(gòu)發(fā)生改變芋浮,則不可避免地影響所有子類(裝飾者和被裝飾者),如果基類改變壳快,勢(shì)必影響對(duì)象的內(nèi)部纸巷。
- 比繼承更加靈活機(jī)動(dòng)的特性,也同時(shí)意味著裝飾模式比繼承更加易于出錯(cuò)眶痰,排錯(cuò)也很困難何暇,對(duì)于多次裝飾的對(duì)象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級(jí)排查凛驮,較為煩瑣裆站,所以只在必要的時(shí)候使用裝飾者模式。
使用場(chǎng)景
- 在不影響其他對(duì)象的情況下黔夭,以動(dòng)態(tài)宏胯、透明的方式給單個(gè)對(duì)象添加職責(zé)。
- 需要?jiǎng)討B(tài)地給一個(gè)對(duì)象增加功能本姥,這些功能可以動(dòng)態(tài)的撤銷肩袍。
- 當(dāng)不能采用繼承的方式對(duì)系統(tǒng)進(jìn)行擴(kuò)充或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)。
4.裝飾模式和代理模式
在上一篇文章設(shè)計(jì)模式之代理模式中我們講到了代理模式婚惫,你會(huì)覺得代理模式和裝飾模式有點(diǎn)像氛赐,都是持有了被代理或者被裝飾對(duì)象的引用魂爪。它們兩個(gè)最大的不同就是裝飾模式對(duì)引用的對(duì)象增加了功能,而代理模式只是對(duì)引用對(duì)象進(jìn)行了控制卻沒有對(duì)引用對(duì)象本身增加功能艰管。