「補課」進行時:設計模式(10)——小明起牀記了解裝飾模式

1. 前文彙總

「補課」進行時:設計模式系列

2. 小明起牀記

小明每天早晨都是起牀困難大戶,大冬天的太冷了,溫暖的被窩緊緊的拉住小明,阻止小明每天早晨的起牀。

鬧鐘響了一遍又一遍,如果再不起牀就要遲到了,遲到了就要扣錢,扣了錢就要喝西北風了。

每天早晨督促小明起牀的根本不是鬧鐘,而是貧窮。

起牀第一件事兒是穿衣服,先傳衣服,再傳褲子,然後穿鞋子,最後穿上一件外套,出門上班。

首先,定義一個抽象的小明,小明是個人,所以定義一個人:

public abstract class Person {
    abstract void dress();
}

每個人早晨起牀都要穿衣服,這裏定義一個穿衣服的方法。

具體的小明上線:

public class Man extends Person {
    @Override
    void dress() {
        System.out.println("先穿衣服");
    }
}

接下來我們要定義一個抽象的裝飾器了,小明要穿的是衣服,我們將衣服抽象成一個類:

public abstract class Clothes extends Person {
    private Person person;

    public Clothes(Person person) {
        this.person = person;
    }

    @Override
    void dress() {
        this.person.dress();
    }
}

接下來是具體的衣服:

public class Trousers extends Clothes {
    public Trousers(Person person) {
        super(person);
    }

    @Override
    void dress() {
        super.dress();
        this.dressTrousers();
    }

    private void dressTrousers() {
        System.out.println("穿上褲子啦!!!");
    }
}

public class Shoes extends Clothes {
    public Shoes(Person person) {
        super(person);
    }
    @Override
    void dress() {
        super.dress();
        this.dressShoes();
    }

    private void dressShoes() {
        System.out.println("穿上鞋子啦!!!");
    }
}

public class Coat extends Clothes {
    public Coat(Person person) {
        super(person);
    }

    @Override
    void dress() {
        super.dress();
        this.dressCoat();
    }

    private void dressCoat() {
        System.out.println("穿上外套啦!!!");
    }
}

最後是一個測試類:

public class Test1 {
    public static void main(String[] args) {
        Person person = new Man();
        person.dress();

        System.out.println("--------------");
        System.out.println("增加褲子適配器");
        person = new Trousers(person);
        person.dress();

        System.out.println("--------------");
        System.out.println("增加鞋子適配器");
        person = new Shoes(person);
        person.dress();

        System.out.println("--------------");
        System.out.println("增加外套適配器");
        person = new Coat(person);
        person.dress();
    }
}

測試結果如下:

先穿衣服
--------------
增加褲子適配器
先穿衣服
穿上褲子啦!!!
--------------
增加鞋子適配器
先穿衣服
穿上褲子啦!!!
穿上鞋子啦!!!
--------------
增加外套適配器
先穿衣服
穿上褲子啦!!!
穿上鞋子啦!!!
穿上外套啦!!!

上面這麼寫有點麻煩,我們可以稍微縮減一下測試類,使用裝飾器嵌套,一次性直接把所有的衣服都穿好:

public class Test2 {
    public static void main(String[] args) {
        Person person = new Coat(new Shoes(new Trousers(new Man())));
        person.dress();
    }
}

3. 裝飾器模式

3.1 定義

裝飾模式(Decorator Pattern)是一種比較常見的模式,其定義如下:

Attachadditional responsibilities to an object dynamically keeping the sameinterface.Decorators provide a flexible alternative to subclassing forextending functionality.(動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更爲靈活。)

  • Component: 抽象構件,是一個接口或者是抽象類,就是定義我們最核心的對象,也就是最原始的對象。
  • ConcreteComponent: 具體構件,是最核心、最原始、最基本的接口或抽象類的實現。
  • Decorator: 通用的裝飾 ConcreteComponent 的裝飾器,其內部必然有一個屬性指向 Component 抽象組件;其實現一般是一個抽象類,主要是爲了讓其子類按照其構造形式傳入一個 Component 抽象組件,這是強制的通用行爲(當然,如果系統中裝飾邏輯單一,並不需要實現許多裝飾器,那麼我們可以直接省略該類,而直接實現一個 具體裝飾器(ConcreteDecorator) 即可)。
  • ConcreteDecorator: Decorator 的具體實現類,理論上,每個 ConcreteDecorator 都擴展了 Component 對象的一種功能。

通用代碼:

public abstract class Component {
    abstract void operate();
}

public class ConcreteComponent extends Component {
    @Override
    void operate() {
        System.out.println("do Something");
    }
}

public abstract class Decorator extends Component {
    private Component component = null;
    // 通過構造函數傳遞被修飾者
    public Decorator(Component component) {
        this.component = component;
    }
    // 委託給被修飾者執行
    @Override
    void operate() {
        this.component.operate();
    }
}

public class ConcreteDecorator1 extends Decorator {
    // 定義被修飾者
    public ConcreteDecorator1(Component component) {
        super(component);
    }
    private void method1() {
        System.out.println("method1 修飾");
    }

    @Override
    void operate() {
        this.method1();
        super.operate();
    }
}

public class ConcreteDecorator2 extends Decorator {
    public ConcreteDecorator2(Component component) {
        super(component);
    }
    private void method2() {
        System.out.println("method2 修飾");
    }

    @Override
    void operate() {
        super.operate();
        this.method2();
    }
}

public class Test {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        // 第一次修飾
        component = new ConcreteDecorator1(component);
        // 第二次修飾
        component = new ConcreteDecorator2(component);
        // 修飾後運行
        component.operate();
    }
}

3.2 優點

裝飾類和被裝飾類可以獨立發展,而不會相互耦合。換句話說, Component 類無須知道 Decorator 類, Decorator 類是從外部來擴展 Component 類的功能,而 Decorator 也不用知道具體的構件。

3.3 缺點

對於裝飾模式記住一點就足夠了:多層的裝飾是比較複雜的。爲什麼會複雜呢?想想看,就像剝洋蔥一樣,至於剝到了最後才發現是最裏層的裝飾出現了問題,想象一下工作量吧,因此,儘量減少裝飾類的數量,以便降低系統的複雜度。

3.3 使用場景

  • 需要擴展一個類的功能,或給一個類增加附加功能。
  • 需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
  • 需要爲一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章