模式設計之行爲模式:裝飾器模式(Decorator模式)詳解

裝飾模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關係的一個替代方案。在軟件開發過程中,有時想用一些現存的組件。這些組件可能只是完成了一些核心功能。但在不改變其結構的情況下,可以動態地擴展其功能。所有這些都可以釆用裝飾器模式來實現。

裝飾器模式的定義與特點

裝飾器(Decorator)模式的定義:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。

裝飾器模式的主要優點有:

  • 裝飾器是繼承的有力補充,比繼承靈活,在不改變原有對象的情況下,動態的給一個對象擴展功能,即插即用
  • 通過使用不用裝飾類及這些裝飾類的排列組合,可以實現不同效果
  • 裝飾器模式完全遵守開閉原則


其主要缺點是:裝飾器模式會增加許多子類,過度使用會增加程序得複雜性。

裝飾器模式的結構與實現

通常情況下,擴展一個類的功能會使用繼承方式來實現。但繼承具有靜態特徵,耦合度高,並且隨着擴展功能的增多,子類會很膨脹。如果使用組合關係來創建一個包裝對象(即裝飾對象)來包裹真實對象,並在保持真實對象的類結構不變的前提下,爲其提供額外的功能,這就是裝飾器模式的目標。下面來分析其基本結構和實現方法。

1. 模式的結構

裝飾器模式主要包含以下角色。

  1. 抽象構件(Component)角色:定義一個抽象接口以規範準備接收附加責任的對象。
  2. 具體構件(ConcreteComponent)角色:實現抽象構件,通過裝飾角色爲其添加一些職責。
  3. 抽象裝飾(Decorator)角色:繼承抽象構件,幷包含具體構件的實例,可以通過其子類擴展具體構件的功能。
  4. 具體裝飾(ConcreteDecorator)角色:實現抽象裝飾的相關方法,並給具體構件對象添加附加的責任。


裝飾器模式的結構圖如圖 1 所示。

圖1 裝飾器模式的結構圖

2. 模式的實現

裝飾器模式的實現代碼如下:

package decorator;

public class DecoratorPattern {
    public static void main(String[] args) {
        Component p = new ConcreteComponent();
        p.operation();
        System.out.println("---------------------------------");
        Component d = new ConcreteDecorator(p);
        d.operation();
    }
}

//抽象構件角色
interface Component {
    public void operation();
}

//具體構件角色
class ConcreteComponent implements Component {
    public ConcreteComponent() {
        System.out.println("創建具體構件角色");
    }

    public void operation() {
        System.out.println("調用具體構件角色的方法operation()");
    }
}

//抽象裝飾角色
class Decorator implements Component {
    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

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

//具體裝飾角色
class ConcreteDecorator extends Decorator {
    public ConcreteDecorator(Component component) {
        super(component);
    }

    public void operation() {
        super.operation();
        addedFunction();
    }

    public void addedFunction() {
        System.out.println("爲具體構件角色增加額外的功能addedFunction()");
    }
}

運行結果:

創建具體構件角色
調用具體構件角色的方法operation()
---------------------------------
調用具體構件角色的方法operation()
爲具體構件角色增加額外的功能addedFunction()

裝飾器模式的應用實例

【例1】用裝飾器模式實現遊戲角色“莫莉卡·安斯蘭”的變身。

分析:在《惡魔戰士》中,遊戲角色“莫莉卡·安斯蘭”的原身是一個可愛少女,但當她變身時,會變成頭頂及背部延伸出蝙蝠狀飛翼的女妖,當然她還可以變爲穿着漂亮外衣的少女。這些都可用裝飾器模式來實現,在本實例中的“莫莉卡”原身有 setImage(String t) 方法決定其顯示方式,而其 變身“蝙蝠狀女妖”和“着裝少女”可以用 setChanger() 方法來改變其外觀,原身與變身後的效果用 display() 方法來顯示(點此下載其原身和變身後的圖片),圖 2 所示是其結構圖。

圖2 遊戲角色“莫莉卡·安斯蘭”的結構圖


程序代碼如下:

package decorator;

import java.awt.*;
import javax.swing.*;

public class MorriganAensland {
    public static void main(String[] args) {
        Morrigan m0 = new original();
        m0.display();
        Morrigan m1 = new Succubus(m0);
        m1.display();
        Morrigan m2 = new Girl(m0);
        m2.display();
    }
}

//抽象構件角色:莫莉卡
interface Morrigan {
    public void display();
}

//具體構件角色:原身
class original extends JFrame implements Morrigan {
    private static final long serialVersionUID = 1L;
    private String t = "Morrigan0.jpg";

    public original() {
        super("《惡魔戰士》中的莫莉卡·安斯蘭");
    }

    public void setImage(String t) {
        this.t = t;
    }

    public void display() {
        this.setLayout(new FlowLayout());
        JLabel l1 = new JLabel(new ImageIcon("src/decorator/" + t));
        this.add(l1);
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

//抽象裝飾角色:變形
class Changer implements Morrigan {
    Morrigan m;

    public Changer(Morrigan m) {
        this.m = m;
    }

    public void display() {
        m.display();
    }
}

//具體裝飾角色:女妖
class Succubus extends Changer {
    public Succubus(Morrigan m) {
        super(m);
    }

    public void display() {
        setChanger();
        super.display();
    }

    public void setChanger() {
        ((original) super.m).setImage("Morrigan1.jpg");
    }
}

//具體裝飾角色:少女
class Girl extends Changer {
    public Girl(Morrigan m) {
        super(m);
    }

    public void display() {
        setChanger();
        super.display();
    }

    public void setChanger() {
        ((original) super.m).setImage("Morrigan2.jpg");
    }
}

運行結果:

裝飾器模式的應用場景

前面講解了關於裝飾器模式的結構與特點,下面介紹其適用的應用場景,裝飾器模式通常在以下幾種情況使用。

  • 當需要給一個現有類添加附加職責,而又不能採用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者採用繼承方式會產生大量的子類。
  • 當需要通過對現有的一組基本功能進行排列組合而產生非常多的功能時,採用繼承關係很難實現,而採用裝飾器模式卻很好實現。
  • 當對象的功能要求可以動態地添加,也可以再動態地撤銷時。


裝飾器模式在 Java 語言中的最著名的應用莫過於 Java I/O 標準庫的設計了。例如,InputStream 的子類 FilterInputStream,OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader,還有 Writer 的子類 BufferedWriter、FilterWriter 以及 PrintWriter 等,它們都是抽象裝飾類。

下面代碼是爲 FileReader 增加緩衝區而採用的裝飾類 BufferedReader 的例子:

BufferedReader in = new BufferedReader(new FileReader("filename.txt"));
String s = in.readLine();

裝飾器模式的擴展

裝飾器模式所包含的 4 個角色不是任何時候都要存在的,在有些應用環境下模式是可以簡化的,如以下兩種情況。

(1) 如果只有一個具體構件而沒有抽象構件時,可以讓抽象裝飾繼承具體構件,其結構圖如圖 4 所示。

(2) 如果只有一個具體裝飾時,可以將抽象裝飾和具體裝飾合併,其結構圖如圖 5 所示。

 

裝飾器模式與代理模式的差別:

對裝飾器模式來說,裝飾者(decorator)和被裝飾者(decoratee)都實現同一個 接口。對代理模式來說,代理類(proxy class)和真實處理的類(real class)都實現同一個接口。他們之間的邊界確實比較模糊,兩者都是對類的方法進行擴展,具體區別如下:

1、裝飾器模式強調的是增強自身,在被裝飾之後你能夠在被增強的類上使用增強後的功能。增強後你還是你,只不過能力更強了而已;代理模式強調要讓別人幫你去做一些本身與你業務沒有太多關係的職責(記錄日誌、設置緩存)。代理模式是爲了實現對象的控制,因爲被代理的對象往往難以直接獲得或者是其內部不想暴露出來。

2、裝飾模式是以對客戶端透明的方式擴展對象的功能,是繼承方案的一個替代方案;代理模式則是給一個對象提供一個代理對象,並由代理對象來控制對原有對象的引用;

3、裝飾模式是爲裝飾的對象增強功能;而代理模式對代理的對象施加控制,但不對對象本身的功能進行增強;

進階閱讀

如果您想深入瞭解裝飾器模式,可猛擊閱讀以下文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章