設計模式——裝飾者模式

Jacob發表於2019-01-19
裝飾模式(Decorator Pattern)
動態地給物件新增一些額外的職責。就功能來說裝飾模式相比生成子類更為靈活。
裝飾模式是在不改變原始類程式碼的基礎上,動態擴充套件個物件的功能。裝飾模式是一種用於替代繼承的技術,它通過一種無須定義子類的方式來給物件動態增加職責,使用物件之間的關聯關係取代類之間的繼承關係。在裝飾模式中引入了裝飾類,在裝飾類中既可以呼叫待裝飾的原有類的方法,還可以增加新的方法,以擴充原有類的功能。這也很好的使用了設計模式的開放-封閉原則。
UML類圖

裝飾模式的結構中包括四種角色:
  • 抽象元件(Component):它是具體元件和抽象裝飾類的共同父類,宣告瞭在具體構件中實現的業務方法,它的引入可以使客戶端以一致的方式處理未被裝飾的物件以及裝飾之後的物件,實現客戶端的透明操作。
  • 具體元件(ConcreteComponent):它是抽象構件類的子類,用於定義具體的元件物件,實現了在抽象元件中宣告的方法,裝飾器可以給它增加額外的職責(方法)。
  • 裝飾類(Decorator):它也是抽象元件類的子類,但它含包含一個指向抽象元件宣告的變數以儲存“被裝飾者” 的引用,通過該引用可以呼叫裝飾之前元件物件的方法,並通過其子類擴充套件該方法,以達到裝飾的目的。
  • 具體裝飾類(ConcreteDecorator):它是抽象裝飾類的子類,負責向元件新增新的職責。每一個具體裝飾類都定義了一些新的行為,它可以呼叫在抽象裝飾類中定義的方法,並可以增加新的方法用以擴充物件的行為。
注意事項:裝飾者模式使用時需要注意以下幾個問題:
  • 儘量保持裝飾類的介面與被裝飾類的介面相同,這樣,對於客戶端而言,無論是裝飾之前的物件還是裝飾之後的物件都可以一致對待。這也就是說,在可能的情況下,我們應該儘量使用透明裝飾模式。
  • 儘量保持具體裝飾類是一個“輕”類,也就是說不要把太多的行為放在具體元件類中,我們可以通過裝飾類對其進行擴充套件。
  • 如果只有一個具體元件類,那麼抽象裝飾類可以作為該具體元件類的直接子類。
使用舉例
我們還用程式設計師舉例,假如程式設計師只會java語言,現在讓他學會設計模式,然後又讓他學會android開發,怎麼辦?我們總不能每次有新的功能都修改程式碼吧,好,用裝飾者模式解決
1、抽象元件
public interface CodeLanguage {
    void useLanguage();
}
2、具體元件
public class CodeJava implements CodeLanguage {
    @Override
    public void useLanguage() {
        System.out.println("使用Java語言程式設計");
    }
}
3、裝飾類
public class CodeDecorator implements CodeLanguage {
    CodeLanguage codeLanguage;
    public CodeDecorator(CodeLanguage codeLanguage) {
        this.codeLanguage = codeLanguage;
    }
    @Override
    public void useLanguage() {
        codeLanguage.useLanguage();
    }
}
4、具體裝飾
public class AndroidDecorator extends CodeDecorator{
    public AndroidDecorator(CodeLanguage codeLanguage) {
        super(codeLanguage);
    }
    @Override
    public void useLanguage() {
        super.useLanguage();
        skill();
    }
    public void skill(){
        System.out.println("學會android開發");
    }
}

public class PatternDecorator extends CodeDecorator {
    public PatternDecorator(CodeLanguage codeLanguage) {
        super(codeLanguage);
    }
    public void useLanguage() {
        super.useLanguage();
        skill();
    }
    public void skill() {
        System.out.println("學會設計模式");
    }
}
5、使用
public class Application  {
    public static void main(String[] args) {
        CodeLanguage codeJava=new CodeJava();
        codeJava.useLanguage();//只會java語言
        CodeLanguage codePattern=new PatternDecorator(codeJava);
        codePattern.useLanguage();//又會java,又會設計模式
        CodeLanguage codeAndroid=new AndroidDecorator(codePattern);
        codeAndroid.useLanguage();//java,設計模式,android都會
    }
}

使用場景
在不影響其他物件的情況下,以動態、透明的方式給單個物件新增職責。
當不能採用繼承的方式對系統進行擴充套件或者採用繼承不利於系統擴充套件和維護時可以使用裝飾模式。
不能採用繼承的情況主要有系統中存在大量獨立的擴充套件,為支援每一種擴充套件或者擴充套件之間的組合將產生大量的子類,使得子類數目呈爆炸性增長;類已定義為不能被繼承(譬如final類)。
總結
裝飾模式優點如下:
● 對於擴充套件一個物件的功能,裝飾模式比繼承更加靈活性,不會導致類的個數急劇增加。
● 可以通過一種動態的方式在執行時選擇不同的具體裝飾類,從而實現不同的行為。
● 可以對一個物件進行多次裝飾,通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合,得到功能更為強大的物件。
● 具體構件類與具體裝飾類可以獨立變化,使用者可以根據需要增加新的具體構件類和具體裝飾類,原有類庫程式碼無須改變,符合“開閉原則”。
裝飾模式缺點如下:
● 使用裝飾模式進行系統設計時將產生很多小物件,這些物件的區別在於它們之間相互連線的方式有所不同,而不是它們的類或者屬性值有所不同,大量小物件的產生勢必會佔用更多的系統資源,在一定程式上影響程式的效能。
● 裝飾模式提供了一種比繼承更加靈活機動的解決方案,但同時也意味著比繼承更加易於出錯,排錯也很困難,對於多次裝飾的物件,除錯時尋找錯誤可能需要逐級排查,較為繁瑣。

參考:






相關文章