裝飾者模式

begin 2018年9月29日14:19:22java

裝飾者模式(Decorator Pattern)

定義

金玉其外,敗絮其中。編程

動態地給一個對象添加一些額外的職責,就增長功能來講,裝飾者模式比生成子類更爲靈活。——《設計模式:可複用面向對象軟件的基礎》設計模式

裝飾者模式是一種對象結構型模式。ide

使用場景

  • 在不影響其餘對象的狀況下,以動態、透明的方式給單個對象添加職責。學習

  • 須要動態地給一個對象增長功能,這些功能也能夠動態地被撤銷。測試

  • 當不能採用繼承的方式對系統進行擴充或者採用繼承不利於系統擴展和維護時。不能採用繼承的狀況主要有兩類:第一類是系統中存在大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加;第二類是由於類定義不能繼承(如final類).網站

角色

抽象構件角色(Component):定義一個對象接口或抽象類,能夠給這些對象動態地添加職責。this

具體構件角色(ConcreteComponent):實際被動態地添加職責的對象。設計

抽象裝飾者角色(Decorator):實現了Component接口,用來擴展Component類的功能,但對於Component來講,是無需知道Decorator的存在的。3d

具體裝飾者角色(ConcreteDecorator):動態地添加職責的對象。

圖示

裝飾者模式(Decorator)結構圖
裝飾者模式(Decorator)結構圖

代碼示例

裝飾者模式(Decorator)代碼示例類圖
裝飾者模式(Decorator)代碼示例類圖

這是一個關於相親的故事。某家有女初長成,七大姑八大姨欲幫忙覓得佳婿,後在各類羣、圈、網站,收集了各類男人的資料。這裏須要一個應用生成收集過來的男人的描述,如:是否有車、是否有房、是否有存款、是否有好品質。而後這個應用了裝飾者模式的應用就誕生了,動態添加男人的描述,最後獲得男人的綜述。

抽象構件角色(Man.java):

// 男人
public interface Man {
    public void getManDesc();
}

具體構件角色(NormalMan.java):

// 普通男人
public class NormalMan implements Man{
    private String name = null;
    
    public NormalMan(String name) {
        this.name = name;
    }
    
    @Override
    public void getManDesc() {
        System.out.print(name + ": ");
    }
}

抽象裝飾者角色(AttachedPropertiesDecorator.java):

// 附加屬性裝飾者
public abstract class AttachedPropertiesDecorator implements Man{
    private Man man;
    
    public AttachedPropertiesDecorator(Man man) {
        this.man = man;
    }
    
    public void getManDesc() {
        man.getManDesc();
    }
}

具體裝飾者角色(CarDecoratorImpl.java、HouseDecoratorImpl.java、DepositDecoratorImpl.java、QualityDecoratorImpl.java):

// 小車裝飾者
public class CarDecoratorImpl extends AttachedPropertiesDecorator{
    private String car = "有車";
    
    public CarDecoratorImpl(Man man) {
        super(man);
    }
    
    public void addCar() {
        System.out.print(car + " ");
    }
    
    @Override
    public void getManDesc() {
        super.getManDesc();
        addCar();
    }
}

// 房子裝飾者
public class HouseDecoratorImpl extends AttachedPropertiesDecorator{
    private String house = "有房";
    
    public HouseDecoratorImpl(Man man) {
        super(man);
    }
    
    public void addHouse() {
        System.out.print(house + " ");
    }
    
    @Override
    public void getManDesc() {
        super.getManDesc();
        addHouse();
    }
}

// 存款裝飾者
public class DepositDecoratorImpl extends AttachedPropertiesDecorator{
    private String deposit = "有存款";
    
    public DepositDecoratorImpl(Man man) {
        super(man);
    }
    
    public void addDeposit() {
        System.out.print(deposit + " ");
    }
    
    @Override
    public void getManDesc() {
        super.getManDesc();
        addDeposit();
    }
}

// 品質裝飾者
public class QualityDecoratorImpl extends AttachedPropertiesDecorator{
    private String quality = "有好品質";
    
    public QualityDecoratorImpl(Man man) {
        super(man);
    }
    
    public void addQuality() {
        System.out.print(quality + " ");
    }
    
    @Override
    public void getManDesc() {
        super.getManDesc();
    }
}

有車、有房、有存款、有品質具體修飾者。

裝飾者模式測試類(DecoratorTest.java):

public class DecoratorTest {

    public static void main(String[] args) {
        Man man = new NormalMan("張三");
        Man man1 = new CarDecoratorImpl(man);
        Man man2 = new HouseDecoratorImpl(man1);
        Man man3 = new DepositDecoratorImpl(man2);
        System.out.println("層層裝飾:");
        man3.getManDesc();
        System.out.println();
        
        System.out.println("重複裝飾(有兩個'有存款'):");
        Man man4 = new DepositDecoratorImpl(man3);
        man4.getManDesc();
        System.out.println();
        
        System.out.println("任意修飾:");
        Man man5 = new QualityDecoratorImpl(man1);
        man5.getManDesc();
        System.out.println();
        
        System.out.println("直接獲得修飾結果:");
        Man man6 = new HouseDecoratorImpl(new DepositDecoratorImpl(new NormalMan("李四")));
        man6.getManDesc();
        System.out.println();

    }
}

運行結果:
運行結果

優勢

一、裝飾者模式和繼承關係的目的都是要擴展對象的功能,可是裝飾模式能夠提供比繼承更多的靈活型。

二、經過使用不一樣的具體裝飾者類及它們不一樣的組合順序,能夠獲得不一樣裝飾後具備不一樣行爲或者狀態的對象。例如上面的CarDecoratorImpl能夠屢次修飾一個男人,證實他有不少車。

三、符合開閉原則。

缺點

一、增長了抽象裝飾者類和具體裝飾者類,必定程度增長了系統的複雜度,加大了系統的學習和理解成本。

二、靈活性也意味着更容易出錯,對於屢次被屢次修飾的對象,調試時尋找錯誤可能須要找到多個地方。

裝飾者模式進階

透明與半透明的裝飾者模式

透明的裝飾者模式:要求客戶端徹底針對抽象編程(依賴倒置原則),裝飾者模式的透明型要求客戶端程序不該該聲明具體構件類型和具體裝飾者類型,而應該所有聲明爲抽象構件類型。固然調用的也是抽象構件類聲明的接口方法了。

Man man = new NormalMan("張三");
Man man1 = new CarDecoratorImpl(man);
Man man2 = new HouseDecoratorImpl(man1);
Man man3 = new DepositDecoratorImpl(man2);

半透明的裝飾者模式:容許用戶在客戶端聲明具體裝飾者類型的對象,容許在具體裝飾者中新增方法且客戶端能夠調用這些新增的方法。

Man man = new NormalMan("張三");
CarDecoratorImpl man1 = new CarDecoratorImpl(man);
HouseDecoratorImpl man2 = new HouseDecoratorImpl(man);
DepositDecoratorImpl man3 = new DepositDecoratorImpl(man);

man1.addCar();
man2.addHouse();
man3.addDeposit();

java.io中裝飾者模式的應用

java I/O類繼承結構圖:
java I/O類繼承結構圖

雖然java I/O中類比較多,可是裝飾者模式的應用方式是差很少的。下面舉一個例子:InputStream及其子類
InputStream及其子類

裝飾者模式對照上圖可得:

抽象構件角色:InputStream,這是一個抽象類。

具體構件角色:ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream(已棄用)、AudioInputStream等,實現了InputStream抽象類。

抽象裝飾者角色:FilterInputStream,實現了抽象構件角色,且成爲具體裝飾者的父類。

具體裝飾者模式:FilterInputStream的子類如經常使用的BufferedInputStream、DataInputStream,還有其餘不經常使用的CheckedInputStream、CipherInputStream、LineNumberInputStream、PushBackInputStream。這裏其實具體的裝飾者還有直接繼承InputStream的ObjectInputStream、SequenceInputStream,這裏涉及到下面介紹的裝飾者模式的簡化

這裏的裝飾者模式大多都是半透明的,具體裝飾者提供了比抽象構件多的方法,客戶端可使用這些方法。

裝飾者模式的簡化

裝飾者有3種簡化模式:

第一種狀況,只有一個具體裝飾者,這樣就不須要抽象裝飾者,具體裝飾者直接繼承抽象構件就能夠了。
裝飾者模式簡化的第一種狀況

第二種狀況,只有一個具體構件,這樣就不須要抽象構件,抽象裝飾者能夠直接繼承具體構件就能夠了。
裝飾者模式簡化的第二種狀況

第三種狀況,就是上面組合起來:只有一個具體構件和一個具體裝飾者,這樣抽象角色都不須要了,具體裝飾者直接繼承集體構件就能夠了。
裝飾者模式簡化的第三種狀況

總結

須要動態給某個對象添加職責,使用裝飾者模式。

end 2018年10月16日15:24:03

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息