重學(xué)設(shè)計(jì)模式之裝飾模式

裝飾模式

定義

裝飾模式又名包裝(Wrapper)模式吹截。裝飾模式以對(duì)客戶(hù)端透明的方式擴(kuò)展對(duì)象的功能,是繼承關(guān)系的一個(gè)替代方案凝危。

在軟件開(kāi)發(fā)中波俄,往往會(huì)有這樣一種需求,我們需要在不改變?cè)到y(tǒng)代碼的時(shí)候蛾默,給一個(gè)類(lèi)增加一個(gè)新的功能或特性懦铺,而Java中單繼承的特性往往會(huì)限制我們對(duì)原代碼的拓展。采用裝飾模式可以使用非繼承的方式且不改變?cè)到y(tǒng)的情況下拓展新的功能/特性趴生。

UML圖

裝飾者模式是一種比較容易理解的設(shè)計(jì)模式阀趴。它的UML圖如下:

裝飾者模式有以下幾個(gè)角色:

  • Component(抽象構(gòu)建):它是具體構(gòu)建與抽象裝飾類(lèi)的共同父類(lèi),定義一個(gè)統(tǒng)一的規(guī)范苍匆,使客戶(hù)端以一致的方法處理裝飾前后的對(duì)象刘急。
  • ConcreteComponent(具體構(gòu)建):抽象構(gòu)建的具體實(shí)現(xiàn),需要被裝飾的對(duì)象浸踩。
  • Decorator(抽象裝飾類(lèi)):它也是抽象構(gòu)建的字類(lèi)叔汁,它用作給具體構(gòu)建增加新的功能/特性,但是具體增加方法由它的字類(lèi)實(shí)現(xiàn)。它維持一個(gè)抽象構(gòu)建的引用据块,通過(guò)該引用調(diào)用未裝飾前具體構(gòu)建的方法码邻。
  • ConcreteDecorator(具體裝飾類(lèi)):抽象裝飾類(lèi)的子類(lèi),實(shí)現(xiàn)向具體構(gòu)建新增功能/特性另假。

我覺(jué)得裝飾模式的核心就是 Decorator(抽象裝飾類(lèi)) 像屋,它通過(guò)一個(gè)持有一個(gè)抽象構(gòu)建的引用來(lái)實(shí)現(xiàn)對(duì)具體構(gòu)建的調(diào)用,且讓子類(lèi)可以新增方法特性。

代碼

//抽象構(gòu)建
public abstract class Component {

    public abstract void operation();
}
//具體構(gòu)建
public class ConcreteComponent extends Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}
//抽象裝飾類(lèi)  核心
public class Decorator extends Component {
    Component component; //持有一個(gè)抽象構(gòu)建的引用

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        component.operation();
    }
}
//具體的裝飾類(lèi)
public class ConcreteDecoratorA extends Decorator {

    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        methodA();
    }

    public void methodA(){
        System.out.println("ConcreteDecoratorA methodA");
    }
}

......

客戶(hù)端調(diào)用

public class Client {

    public static void main(String[] args) {
        Component concreteComponent, concreteDecoratorA, concreteDecoratorB;

        concreteComponent = new ConcreteComponent();
        concreteComponent.operation();
        System.out.println("----------------------------");

        concreteDecoratorA = new ConcreteDecoratorA(concreteComponent);
        concreteDecoratorA.operation();
        System.out.println("----------------------------");

        concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
        concreteDecoratorB.operation();
        System.out.println("----------------------------");
    }
}

客戶(hù)端調(diào)用結(jié)果:

ConcreteComponent operation
----------------------------
ConcreteComponent operation
ConcreteDecoratorA methodA
----------------------------
ConcreteComponent operation
ConcreteDecoratorA methodA
ConcreteDecoratorB methodB
----------------------------

從上面可以看到,我在客戶(hù)端分別創(chuàng)建了三個(gè)Component的子類(lèi)齿椅,第一個(gè)是具體構(gòu)建類(lèi),第二個(gè)是具體裝飾類(lèi)A凌受,持有具體構(gòu)建類(lèi)的引用,第三個(gè)是具體裝飾類(lèi)B思杯,持有具體裝飾類(lèi)A的引用胜蛉,調(diào)用同一個(gè)方法后,可以從輸出結(jié)果中看到色乾,每一個(gè)裝飾類(lèi)都實(shí)現(xiàn)了被裝飾類(lèi)的方法同時(shí)可以裝飾自己的方法誊册。

實(shí)例

老規(guī)矩,還是用一個(gè)實(shí)例來(lái)演練一番杈湾。

某軟件公司欲開(kāi)發(fā)了一個(gè)數(shù)據(jù)加密模塊解虱,可以對(duì)字符串進(jìn)行加密。最簡(jiǎn)單的加密算法通過(guò)對(duì)字母進(jìn)行移位來(lái)實(shí)現(xiàn)漆撞,同時(shí)還提供了稍復(fù)雜的逆向輸出加密殴泰,還提供了更為高級(jí)的求模加密。用戶(hù)先使用最簡(jiǎn)單的加密算法對(duì)字符串進(jìn)行加密浮驳,如果覺(jué)得還不夠可以對(duì)加密之后的結(jié)果使用其他加密算法進(jìn)行二次加密悍汛,當(dāng)然也可以進(jìn)行第三次加密。試使用裝飾模式設(shè)計(jì)該多重加密系統(tǒng)至会。

分析需求离咐,字母位移加密是原始的加密方法,其他算法的二次三次加密都是對(duì)原加密算法對(duì)裝飾奉件。

UML圖

根據(jù)需求繪制UML圖如下

基本上都是套用定義的UML圖宵蛀,根據(jù)UML圖可以很清楚的看清整個(gè)軟件架構(gòu)。

代碼

// 抽象構(gòu)建
public abstract class EncryptComponent {

    abstract String encrypt(String str);
}
//原始加密算法
public class OriginalEncrypt extends EncryptComponent {

    @Override
    String encrypt(String str) {
        System.out.println("對(duì)字符串 \'"+str+"\' 使用原始加密   =====>  原始加密結(jié)果");
        String encryptStr = "原始加密結(jié)果";
        return encryptStr;
    }
}
//抽象裝飾類(lèi)
public class EncryptDecorator extends EncryptComponent {

    EncryptComponent encryptComponent;

    public EncryptDecorator(EncryptComponent encryptComponent) {
        this.encryptComponent = encryptComponent;
    }

    @Override
    String encrypt(String str) {
        return encryptComponent.encrypt(str);
    }
}
//另一種加密算法A
public class OtherAEncrypt extends EncryptDecorator {

    public OtherAEncrypt(EncryptComponent encryptComponent) {
        super(encryptComponent);
    }

    @Override
    String encrypt(String str) {
        return otherAEncrypt(super.encrypt(str));
    }

    public String otherAEncrypt(String str){
        System.out.println("對(duì)字符串 \'"+str+"\' 使用OtherA加密   =====>  OtherA加密結(jié)果");
        return "OtherA 加密結(jié)果";
    }
}

//另一種加密算法B 代碼類(lèi)似  可以到我的git上去clone
...

客戶(hù)端測(cè)試

public class Client {

    public static void main(String[] args) {
        EncryptComponent originalEncrypt, otherAEncrypt, otherBEncrypt;
        String result;

        originalEncrypt = new OriginalEncrypt();
        result = originalEncrypt.encrypt("初始數(shù)據(jù)");
        System.out.println("-----------------------------------------------");

        otherAEncrypt = new OtherAEncrypt(originalEncrypt);
        result = otherAEncrypt.encrypt("初始數(shù)據(jù)");
        System.out.println("-----------------------------------------------");

        otherBEncrypt = new OtherBEncrypt(originalEncrypt);
        result = otherBEncrypt.encrypt("初始數(shù)據(jù)");
        System.out.println("-----------------------------------------------");

        otherBEncrypt = new OtherBEncrypt(otherAEncrypt);
        result = otherBEncrypt.encrypt("初始數(shù)據(jù)");
        System.out.println("-----------------------------------------------");

    }
}

結(jié)果

對(duì)字符串 '初始數(shù)據(jù)' 使用原始加密   =====>  原始加密結(jié)果
-----------------------------------------------
對(duì)字符串 '初始數(shù)據(jù)' 使用原始加密   =====>  原始加密結(jié)果
對(duì)字符串 '原始加密結(jié)果' 使用OtherA加密   =====>  OtherA加密結(jié)果
-----------------------------------------------
對(duì)字符串 '初始數(shù)據(jù)' 使用原始加密   =====>  原始加密結(jié)果
對(duì)字符串 '原始加密結(jié)果' 使用OtherB加密   =====>  OtherB加密結(jié)果
-----------------------------------------------
對(duì)字符串 '初始數(shù)據(jù)' 使用原始加密   =====>  原始加密結(jié)果
對(duì)字符串 '原始加密結(jié)果' 使用OtherA加密   =====>  OtherA加密結(jié)果
對(duì)字符串 'OtherA 加密結(jié)果' 使用OtherB加密   =====>  OtherB加密結(jié)果
-----------------------------------------------

相信大部分同學(xué)都可以猜到結(jié)果及邏輯處理過(guò)程县貌。

簡(jiǎn)化

其實(shí)當(dāng)系統(tǒng)中具體構(gòu)建只有一個(gè)的時(shí)候术陶,我們可以省略抽象構(gòu)建,讓具體構(gòu)建同時(shí)擔(dān)任這兩個(gè)角色煤痕,如下:

如圖梧宫,可以簡(jiǎn)化系統(tǒng)的復(fù)雜度接谨,去處冗余的代碼。具體代碼就不貼了塘匣,相信對(duì)于大家應(yīng)該不難理解脓豪。

半透明裝飾模式

通過(guò)上面的代碼,相信大部分同學(xué)都會(huì)感覺(jué)有點(diǎn)熟悉忌卤,這種一個(gè)類(lèi)包裹另一個(gè)類(lèi)的套路像不像JAVA 數(shù)據(jù)IO 操作扫夜。

FileInputStream fileInputStream = new FileInputStream("DecoratorPattern/test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

的確,JAVA的IO操作就是裝飾模式的一個(gè)具體實(shí)現(xiàn)驰徊,在JAVA中裝飾模式是使用十分頻繁的一種設(shè)計(jì)模式历谍。

但是,大家有沒(méi)有發(fā)現(xiàn)這里的裝飾模式與我們前面的例子有什么不同地方呢辣垒?

在上面的例子中,我們?cè)诳蛻?hù)端聲明實(shí)例時(shí)

 EncryptComponent originalEncrypt, otherAEncrypt, otherBEncrypt;

所有的具體構(gòu)建或者具體裝飾都是以抽象構(gòu)建來(lái)定義印蔬,因?yàn)橥ㄟ^(guò)UML圖我們知道它們都是抽象構(gòu)建的子類(lèi)勋桶。這種對(duì)于客戶(hù)端是而言是完全針對(duì)抽象編程,也就是透明裝飾模式

但是所有的具體構(gòu)建或者具體裝飾都以抽象構(gòu)建來(lái)定義會(huì)導(dǎo)致一個(gè)問(wèn)題侥猬,它們都只能調(diào)用抽象構(gòu)建中定義的方法例驹,而在實(shí)際開(kāi)發(fā)中,我們往往需要單獨(dú)使用具體裝飾類(lèi)中的方法退唠,這個(gè)時(shí)候使用抽象構(gòu)建來(lái)定義具體裝飾類(lèi)就不合適了鹃锈。

當(dāng)我們需要單獨(dú)使用具體裝飾類(lèi)中的方法時(shí),我們就需要單獨(dú)以具體裝飾類(lèi)來(lái)定義聲明瞧预,這種就是半透明的裝飾模式屎债。

小結(jié)

裝飾模式降低了系統(tǒng)的耦合度,可以動(dòng)態(tài)增加或刪除對(duì)象的職責(zé)垢油,并使得需要裝飾的具體構(gòu)件類(lèi)和具體裝飾類(lèi)可以獨(dú)立變化盆驹,以便增加新的具體構(gòu)件類(lèi)和具體裝飾類(lèi)。對(duì)于拓展一個(gè)對(duì)象的功能滩愁,裝飾模式比繼承更加靈活躯喇。通過(guò)動(dòng)態(tài)的方式來(lái)拓展一個(gè)對(duì)象的功能,且可以進(jìn)行多次不同的裝飾硝枉。

在實(shí)際開(kāi)發(fā)中廉丽,透明裝飾模式的設(shè)計(jì)難度較大,而且使用不夠靈活妻味。而半透明裝飾模式可以給系統(tǒng)帶來(lái)更大的靈活性正压,且設(shè)計(jì)相對(duì)簡(jiǎn)單。


源碼:https://github.com/lichenming0516/DesignPattern

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末弧可,一起剝皮案震驚了整個(gè)濱河市蔑匣,隨后出現(xiàn)的幾起案子劣欢,更是在濱河造成了極大的恐慌,老刑警劉巖裁良,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凿将,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡价脾,警方通過(guò)查閱死者的電腦和手機(jī)牧抵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)侨把,“玉大人犀变,你說(shuō)我怎么就攤上這事∏锉” “怎么了获枝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)骇笔。 經(jīng)常有香客問(wèn)我省店,道長(zhǎng),這世上最難降的妖魔是什么笨触? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任懦傍,我火速辦了婚禮,結(jié)果婚禮上芦劣,老公的妹妹穿的比我還像新娘粗俱。我一直安慰自己,他們只是感情好虚吟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布寸认。 她就那樣靜靜地躺著,像睡著了一般串慰。 火紅的嫁衣襯著肌膚如雪废麻。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天模庐,我揣著相機(jī)與錄音烛愧,去河邊找鬼。 笑死掂碱,一個(gè)胖子當(dāng)著我的面吹牛怜姿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疼燥,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沧卢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了醉者?” 一聲冷哼從身側(cè)響起但狭,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤披诗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后立磁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體呈队,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年唱歧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宪摧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颅崩,死狀恐怖几于,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沿后,我是刑警寧澤沿彭,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站尖滚,受9級(jí)特大地震影響膝蜈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熔掺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望非剃。 院中可真熱鬧置逻,春花似錦、人聲如沸备绽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肺素。三九已至恨锚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倍靡,已是汗流浹背猴伶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留塌西,地道東北人他挎。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捡需,于是被迫代替她去往敵國(guó)和親办桨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理站辉,服務(wù)發(fā)現(xiàn)呢撞,斷路器损姜,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用殊霞、多...
    MinoyJet閱讀 3,947評(píng)論 1 15
  • 設(shè)計(jì)模式基本原則 開(kāi)放-封閉原則(OCP)摧阅,是說(shuō)軟件實(shí)體(類(lèi)、模塊脓鹃、函數(shù)等等)應(yīng)該可以拓展逸尖,但是不可修改。開(kāi)-閉原...
    西山薄涼閱讀 3,798評(píng)論 3 14
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法瘸右,類(lèi)相關(guān)的語(yǔ)法娇跟,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法太颤,異常的語(yǔ)法苞俘,線(xiàn)程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,639評(píng)論 18 399
  • every time you kiss me we are one eversleeping 憂(yōu)傷還是快樂(lè) 風(fēng)之甬...
    棲惶閱讀 70評(píng)論 0 0