在現實生活中,常常需要對現有產品增加新的功能或美化其外觀,如房子裝修、相片加相框等。在軟件開發過程中,有時想用一些現存的組件。這些組件可能只是完成了一些核心功能。但在不改變其結構的情況下,可以動態地擴展其功能。所有這些都可以釆用裝飾模式來實現。
裝飾器模式的定義與特點
裝飾(Decorator
)模式的定義
:指在不改變現有對象結構的情況下,動態地給該對象增加一些職責(即增加其額外功能)的模式,它屬於對象結構型模式。
裝飾(Decorator)模式的主要優點
有:
1.採用裝飾模式擴展對象的功能比採用繼承方式更加靈活。
2.可以設計出多個不同的具體裝飾類,創造出多個不同行爲的組合,實現不同的效果。
其主要缺點
是:
裝飾模式增加了許多子類,如果過度使用會使程序變得很複雜。
裝飾器模式的結構
通常情況下,擴展一個類的功能會使用繼承方式來實現。但繼承具有靜態特徵,耦合度高,並且隨着擴展功能的增多,子類會很膨脹。如果使用組合關係來創建一個包裝對象(即裝飾對象)來包裹真實對象,並在保持真實對象的類結構不變的前提下,爲其提供額外的功能,這就是裝飾模式的目標。
裝飾模式主要包含以下角色:
抽象組件(Component
)角色:可以是一個接口或抽象類以規定被裝飾對象的行爲。
具體組件(ConcreteComponent
)角色:實現/繼承Component的具體對象,即被裝飾對象。
抽象裝飾(Decorator
)角色:通常是裝飾ConcreteComponent的裝飾器,其內部必然有一個屬性指向Component抽象組件;其實現一般是一個抽象類,主要爲了讓其子類按照其構造形式傳入一個Component抽象組件,這是強制的通用行爲。(當然,如果系統中裝飾邏輯 單一,並不需要實現許多裝飾器,那麼我們可以直接忽略該類,而直接實現一個具體的裝飾器ConcreteDecorator即可)
具體裝飾(ConcreteDecorator
)角色:Decorator的具體實現類,理論上每個ConcreteDecorator擴展了Component對象的一種功能。
裝飾器模式的實現
裝飾器模式的核心是功能的擴展。使用裝飾器模式可以透明且動態的擴展類的功能。
其實現原理爲:讓裝飾器實現被包裝類(ConcreteComponent)相同的接口(Component)使得裝飾器與被擴展類類型一致,並在構造函數中傳入該接口(Component)對象,然後就可以在接口需要實現的方法中在被包裝類的現有功能上添加新功能了。而且,由於裝飾器與被包裝類屬於同一類型(均爲Component),切構造函數的參數其實現接口類(Component),因此裝飾器模式具備嵌套擴展功能,這樣我們就可以使用裝飾器模式一層一層的對最底層包裝類進行功能擴展。
舉例:買煎餅果子,不管你是加香腸還是加雞蛋,最終都是一個煎餅果子吧,只是其包裝的不同而已。
/**
*抽象組件(`Component`):煎餅
*/
public abstract class Battercake {
protected abstract String getMsg();//煎餅的信息
protected abstract int getPrice();//煎餅的價錢
}
/**
* 具體組件(`ConcreteComponent`):沒有被包裝的煎餅(不加腸,不加蛋)
*/
public class BaseBattercake extends Battercake{
protected String getMsg(){ return "煎餅";}
public int getPrice(){ return 5;}
}
/**
*抽象裝飾類(`Decorator`):裝飾煎餅的裝飾器
*/
public abstract class BattercakeDecorator extends Battercake{
private Battercake battercake;
public BattercakeDecorator(Battercake battercake) {
this.battercake = battercake;
}
protected String getMsg(){ return this.battercake.getMsg();}
public int getPrice(){ return this.battercake.getPrice();}
}
/**
* 具體裝飾類(`ConcreteDecorator`):給煎餅加個蛋,價格也貴了
*/
public class EggDecorator extends BattercakeDecorator{
public EggDecorator(Battercake battercake) {
super(battercake);
}
protected String getMsg(){ return super.getMsg() + "夾1個雞蛋";}
public int getPrice(){ return super.getPrice() + 1;}
}
/**
* 具體裝飾類(`ConcreteDecorator`):給煎餅加根香腸,價格加2塊
*/
public class SauageDecorator extends BattercakeDecorator{
public SauageDecorator(Battercake battercake) {
super(battercake);
}
protected String getMsg(){ return super.getMsg() + "夾1根香腸";}
public int getPrice(){ return super.getPrice() + 2;}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
Battercake battercake;
battercake = new BaseBattercake();
System.out.println(battercake.getMsg() + ",總價" + battercake.getPrice());
battercake = new EggDecorator(battercake);
System.out.println(battercake.getMsg() + ",總價" + battercake.getPrice());
battercake = new SauageDecorator(battercake);
System.out.println(battercake.getMsg() + ",總價" + battercake.getPrice());
}
}
裝飾器模式的應用場景
裝飾器模式在我們生活中非常常見:給煎餅加雞蛋,給蛋糕加點水果,給房子裝修等等。
爲對象擴展一些額外的職責,裝飾器模式在代碼中適用於以下場景:
1.用於擴展一個類的功能或給一個類添加額外的職責。
2.動態的給一個對象添加功能,這些功能可以再動態的撤銷。
3.需要爲一批的兄弟類進行改裝或加裝功能。
裝飾模式在 Java 語言中的最著名的應用莫過於 Java I/O 標準庫的設計了。例如,InputStream 的子類 FilterInputStream,OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader,還有 Writer 的子類 BufferedWriter、FilterWriter 以及PrintWriter 等,它們都是抽象裝飾類。