重學設計模式之裝飾模式

裝飾模式

定義

裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方案。

在軟件開發中,往往會有這樣一種需求,我們需要在不改變原系統代碼的時候,給一個類增加一個新的功能或特性,而Java中單繼承的特性往往會限制我們對原代碼的拓展。采用裝飾模式可以使用非繼承的方式且不改變原系統的情況下拓展新的功能/特性。

UML圖

裝飾者模式是一種比較容易理解的設計模式。它的UML圖如下:

裝飾者模式有以下幾個角色:

  • Component(抽象構建):它是具體構建與抽象裝飾類的共同父類,定義一個統一的規范,使客戶端以一致的方法處理裝飾前后的對象。
  • ConcreteComponent(具體構建):抽象構建的具體實現,需要被裝飾的對象。
  • Decorator(抽象裝飾類):它也是抽象構建的字類,它用作給具體構建增加新的功能/特性,但是具體增加方法由它的字類實現。它維持一個抽象構建的引用,通過該引用調用未裝飾前具體構建的方法。
  • ConcreteDecorator(具體裝飾類):抽象裝飾類的子類,實現向具體構建新增功能/特性。

我覺得裝飾模式的核心就是 Decorator(抽象裝飾類) ,它通過一個持有一個抽象構建的引用來實現對具體構建的調用,且讓子類可以新增方法特性。

代碼

//抽象構建
public abstract class Component {

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

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

    @Override
    public void operation() {
        component.operation();
    }
}
//具體的裝飾類
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");
    }
}

......

客戶端調用

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("----------------------------");
    }
}

客戶端調用結果:

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

從上面可以看到,我在客戶端分別創建了三個Component的子類,第一個是具體構建類,第二個是具體裝飾類A,持有具體構建類的引用,第三個是具體裝飾類B,持有具體裝飾類A的引用,調用同一個方法后,可以從輸出結果中看到,每一個裝飾類都實現了被裝飾類的方法同時可以裝飾自己的方法。

實例

老規矩,還是用一個實例來演練一番。

某軟件公司欲開發了一個數據加密模塊,可以對字符串進行加密。最簡單的加密算法通過對字母進行移位來實現,同時還提供了稍復雜的逆向輸出加密,還提供了更為高級的求模加密。用戶先使用最簡單的加密算法對字符串進行加密,如果覺得還不夠可以對加密之后的結果使用其他加密算法進行二次加密,當然也可以進行第三次加密。試使用裝飾模式設計該多重加密系統。

分析需求,字母位移加密是原始的加密方法,其他算法的二次三次加密都是對原加密算法對裝飾。

UML圖

根據需求繪制UML圖如下

基本上都是套用定義的UML圖,根據UML圖可以很清楚的看清整個軟件架構。

代碼

// 抽象構建
public abstract class EncryptComponent {

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

    @Override
    String encrypt(String str) {
        System.out.println("對字符串 \'"+str+"\' 使用原始加密   =====>  原始加密結果");
        String encryptStr = "原始加密結果";
        return encryptStr;
    }
}
//抽象裝飾類
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("對字符串 \'"+str+"\' 使用OtherA加密   =====>  OtherA加密結果");
        return "OtherA 加密結果";
    }
}

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

客戶端測試

public class Client {

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

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

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

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

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

    }
}

結果

對字符串 '初始數據' 使用原始加密   =====>  原始加密結果
-----------------------------------------------
對字符串 '初始數據' 使用原始加密   =====>  原始加密結果
對字符串 '原始加密結果' 使用OtherA加密   =====>  OtherA加密結果
-----------------------------------------------
對字符串 '初始數據' 使用原始加密   =====>  原始加密結果
對字符串 '原始加密結果' 使用OtherB加密   =====>  OtherB加密結果
-----------------------------------------------
對字符串 '初始數據' 使用原始加密   =====>  原始加密結果
對字符串 '原始加密結果' 使用OtherA加密   =====>  OtherA加密結果
對字符串 'OtherA 加密結果' 使用OtherB加密   =====>  OtherB加密結果
-----------------------------------------------

相信大部分同學都可以猜到結果及邏輯處理過程。

簡化

其實當系統中具體構建只有一個的時候,我們可以省略抽象構建,讓具體構建同時擔任這兩個角色,如下:

如圖,可以簡化系統的復雜度,去處冗余的代碼。具體代碼就不貼了,相信對于大家應該不難理解。

半透明裝飾模式

通過上面的代碼,相信大部分同學都會感覺有點熟悉,這種一個類包裹另一個類的套路像不像JAVA 數據IO 操作。

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

的確,JAVA的IO操作就是裝飾模式的一個具體實現,在JAVA中裝飾模式是使用十分頻繁的一種設計模式。

但是,大家有沒有發現這里的裝飾模式與我們前面的例子有什么不同地方呢?

在上面的例子中,我們在客戶端聲明實例時

 EncryptComponent originalEncrypt, otherAEncrypt, otherBEncrypt;

所有的具體構建或者具體裝飾都是以抽象構建來定義,因為通過UML圖我們知道它們都是抽象構建的子類。這種對于客戶端是而言是完全針對抽象編程,也就是透明裝飾模式

但是所有的具體構建或者具體裝飾都以抽象構建來定義會導致一個問題,它們都只能調用抽象構建中定義的方法,而在實際開發中,我們往往需要單獨使用具體裝飾類中的方法,這個時候使用抽象構建來定義具體裝飾類就不合適了。

當我們需要單獨使用具體裝飾類中的方法時,我們就需要單獨以具體裝飾類來定義聲明,這種就是半透明的裝飾模式。

小結

裝飾模式降低了系統的耦合度,可以動態增加或刪除對象的職責,并使得需要裝飾的具體構件類和具體裝飾類可以獨立變化,以便增加新的具體構件類和具體裝飾類。對于拓展一個對象的功能,裝飾模式比繼承更加靈活。通過動態的方式來拓展一個對象的功能,且可以進行多次不同的裝飾。

在實際開發中,透明裝飾模式的設計難度較大,而且使用不夠靈活。而半透明裝飾模式可以給系統帶來更大的靈活性,且設計相對簡單。


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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,782評論 18 139
  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,960評論 1 15
  • 設計模式基本原則 開放-封閉原則(OCP),是說軟件實體(類、模塊、函數等等)應該可以拓展,但是不可修改。開-閉原...
    西山薄涼閱讀 3,833評論 3 14
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,718評論 18 399
  • every time you kiss me we are one eversleeping 憂傷還是快樂 風之甬...
    棲惶閱讀 116評論 0 0