設計模式之裝飾者模式

0x01.定義與類型

  • 定義:裝飾模式指的是在沒必要改變原類文件和使用繼承的狀況下,動態地擴展一個對象的功能。它是經過建立一個包裝對象,也就是裝飾來包裹真實的對象。
  • 特色:html

    • 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
    • 裝飾對象包含一個真實對象的引用(reference)
    • 裝飾對象接受全部來自客戶端的請求。它把這些請求轉發給真實的對象。
    • 裝飾對象能夠在轉發這些請求之前或之後增長一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就能夠在外部增長附加的功能。在面向對象的設計中,一般是經過繼承來實現對給定類的功能擴展。
  • 類型:結構型
  • UML類圖

clipboard.png

  • 樣例實現
/**
* 組件類接口
*/
public interface IComponent {
    void operation();
}

/**
* 具體組件類的具體業務邏輯實現
*/
public class Component implements IComponent {
    @Override
    public void operation() {
        System.out.println("component operation!");
    }
}

/**
* 裝飾器的抽象類
*/
public abstract class ADecorator implements IComponent {
    /**
    * 關鍵在於這個組合組件接口對象
    */
    private IComponent component;

    public ADecorator(IComponent component) {
        this.component = component;
    }

    @Override
    public void operation () {
        component.operation();
    }
}

/**
* 裝飾器具體實現1
*/
public class Decorator1 extends ADecorator {
    public Decorator1(IComponent component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("decorator1");
    }
}

/**
* 裝飾器具體實現2
*/
public class Decorator2 extends ADecorator {

    public Decorator2(IComponent component) {
        super(component);
    }

    @Override
    public void operation() {
        super.operation();
        System.out.println("decorator2");
    }
}
  • 測試與應用類
/**
* 應用與測試類
*/
public class Test {

    public static void main(String[] args) {
        //應用類
        IComponent component;

        //初始化
        component = new Component();

        //裝飾
        component = new Decorator1(component);
        component = new Decorator2(component);

        //具體方法的調用
        component.operation();
    }
}
  • 輸出結果
component operation!
decorator1
decorator2
  • 裝飾着模式中的各組件:java

    • 抽象構件(IComponent)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
    • 具體構件(Component)角色:定義一個將要接收附加責任的類。
    • 裝飾(ADecorator)角色:持有一個構件(IComponent)對象的實例,並定義一個與抽象構件接口一致的接口。
    • 具體裝飾(Decorator1/Decorator2)角色:負責給構件對象「貼上」附加的責任。

0x02.使用場景

  • 須要擴展一個類的功能,或給一個類添加附加職責。
  • 須要動態的給一個對象添加功能,這些功能能夠再動態的撤銷。
  • 須要增長由一些基本功能的排列組合而產生的很是大量的功能,從而使繼承關係變的不現實。
  • 當不能採用生成子類的方法進行擴充時。一種狀況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增加。另外一種狀況多是由於類定義被隱藏,或類定義不能用於生成子類。

0x03.優勢

  • Decorator模式與繼承關係的目的都是要擴展對象的功能,可是Decorator能夠提供比繼承更多的靈活性。
  • 經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,設計師能夠創造出不少不一樣行爲的組合。
  • 符合開閉原則

0x04.缺點

  • 這種比繼承更加靈活機動的特性,也同時意味着更加多的複雜性。
  • 裝飾模式會致使設計中出現許多小類,若是過分使用,會使程序變得很複雜。
  • 裝飾模式是針對抽象組件(Component)類型編程。可是,若是你要針對具體組件編程時,就應該從新思考你的應用架構,以及裝飾者是否合適。固然也能夠改變Component接口,增長新的公開的行爲,實現「半透明」的裝飾者模式。在實際項目中要作出最佳選擇

0x05.舉例實現裝飾着模式

咱們買煎餅果子能夠選擇加香腸,仍是加雞蛋。煎餅果子就是咱們的組件類,而加雞蛋加培根則是裝飾者的具體實現類.
  • 裝飾者模式Java實現
/**
 * 煎餅的抽象類,也就是組建類
 */
public abstract class ABattercake {

    /**
     * 最後的描述
     * @return
     */
    protected abstract String getDesc ();

    /**
     * 總共的價格
     * @return
     */
    protected abstract int cost ();

}


/**
 * 煎餅的具體實現
 */
public class Battercake extends ABattercake {
    @Override
    protected String getDesc() {
        return "煎餅";
    }

    @Override
    protected int cost() {
        return 8;
    }
}

/**
 * 裝飾着的抽象類
 */
public abstract class AbstractDecorator extends ABattercake {

    //組合的煎餅對象
    private ABattercake aBattercake;

    public AbstractDecorator(ABattercake aBattercake) {
        this.aBattercake = aBattercake;
    }

    protected abstract void doSomething();

    @Override
    protected String getDesc() {
        return aBattercake.getDesc();
    }

    @Override
    protected int cost() {
        return aBattercake.cost();
    }
}

/**
 * 裝飾者的具體實現
 */
public class EggDecorator extends AbstractDecorator {

    public EggDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDesc() {
        return super.getDesc() + " 加一個雞蛋";
    }

    @Override
    protected int cost() {
        return super.cost() + 1;
    }
}

/**
 * 裝飾者的具體實現
 */
public class SausageDecorator extends AbstractDecorator {

    public SausageDecorator(ABattercake aBattercake) {
        super(aBattercake);
    }

    @Override
    protected void doSomething() {

    }

    @Override
    protected String getDesc() {
        return super.getDesc() + " 加一根香腸";
    }

    @Override
    protected int cost() {
        return super.cost() + 2;
    }
}
  • 應用的測試類
public class Test {

    public static void main(String[] args) {
        ABattercake aBattercake;

        aBattercake = new Battercake();
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new EggDecorator(aBattercake);
        aBattercake = new SausageDecorator(aBattercake);

        System.out.println(aBattercake.getDesc() + "銷售價格:" + aBattercake.cost());
    }
}
  • 輸入結果
煎餅 加一個雞蛋 加一個雞蛋 加一根香腸銷售價格:12
  • 樣例UML類圖

clipboard.png

  • 注意:裝飾者最上層的類是否使用抽象類,這個是看業務的。

0x06.相關設計模式

  • 裝飾者模式和代理模式git

    • 裝飾者模式:關注再一個對象上動態添加方法
    • 代理模式:關注再對代理對象的控制訪問,能夠對客戶隱藏被代理類的信息
  • 裝飾着模式和適配器模式github

    • 都叫包裝模式
    • 關於新職責:適配器也能夠在轉換時增長新的職責,但主要目的不在此。裝飾者模式主要是給被裝飾者增長新職責的。
    • 關於原接口:適配器模式是用新接口來調用原接口,原接口對新系統是不可見或者說不可用的。裝飾者模式原封不動的使用原接口,系統對裝飾的對象也經過原接口來完成使用。(增長新接口的裝飾者模式能夠認爲是其變種--「半透明」裝飾者)
    • 關於其包裹的對象:適配器是知道被適配者的詳細狀況的(就是那個類或那個接口)。裝飾者只知道其接口是什麼,至於其具體類型(是基類仍是其餘派生類)只有在運行期間才知道。

0x07.源碼中的裝飾者

  • BufferedReader
  • BufferInputStream/BufferOutputStream

clipboard.png

  • Spring. TransactionAwareCacheDecorator
  • Mybatis. Cache

0x08.源碼

設計模式之裝飾着模式: https://github.com/sigmako/design-pattern/tree/master/decorator編程

0x09.參考

相關文章
相關標籤/搜索