1. 程式人生 > 實用技巧 >「補課」進行時:設計模式(10)——小明起床記了解裝飾模式

「補課」進行時:設計模式(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 使用場景

  • 需要擴充套件一個類的功能,或給一個類增加附加功能。
  • 需要動態地給一個物件增加功能,這些功能可以再動態地撤銷。
  • 需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。