裝飾器模式
一. 什么是裝飾器模式?
我們都知道裝飾, 元旦, 聖誕節, 我們都需要裝飾, 渲染節日氣氛. . 所謂裝飾, 就是在原來的基礎上加東西.
裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是對現有類的包裝。
這種模式創建了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。
我們通過下面的實例來演示裝飾器模式的用法。其中,我們將把聖誕節的房間在原來的基礎上裝飾上了氣球。
我們來看看UML圖:
最開始有房子, 房子有平房, 樓房, 別墅, 我們會打掃房間.
/**
* 房子抽象類
*/
public interface IHouse {
void clean();
}
/**
* 平房
*/
public class Bungalow implements IHouse{
@Override
public void clean() {
// 打掃房間
}
}
/**
* 樓房
*/
public class HighRiseBuilding implements IHouse{
@Override
public void clean() {
// 打掃房間
}
}
/**
* 別墅
*/
public class Villa implements IHouse{
@Override
public void clean() {
// 打掃房間
}
}
這時, 遇到節假日, 比如:聖誕節, 元旦, 我們會裝飾房間, 給房間布置氣球等.
我們可以采用裝飾器設計模式. 這時, 裝飾的主體依然房子. 裝飾就像是一個殼子, 套在房子的外面.
/**
* 房子抽象類
*/
public interface IHouse {
void clean();
}
/**
* 平房
*/
public class Bungalow implements IHouse{
@Override
public void clean() {
// 打掃房間
System.out.println("我是平房: 打掃房間");
}
}
/**
* 樓房
*/
public class HighRiseBuilding implements IHouse{
@Override
public void clean() {
// 打掃房間
System.out.println("我是樓房: 打掃房間");
}
}
/**
* 別墅
*/
public class Villa implements IHouse{
@Override
public void clean() {
// 打掃房間
System.out.println("我是別墅: 打掃房間");
}
}
/**
* 房間裝飾器
*/
public abstract class DecoratorHouse implements IHouse {
// 被裝飾的房子
protected IHouse decoratedHouse;
public DecoratorHouse(IHouse decoratedHouse) {
this.decoratedHouse = decoratedHouse;
}
}
public class BalloonDecoratorHouse extends DecoratorHouse {
public BalloonDecoratorHouse(IHouse decoratedHouse) {
super(decoratedHouse);
}
@Override
public void clean() {
// 打掃房間
super.decoratedHouse.clean();
ballonDecorate();
}
public void ballonDecorate() {
// 用氣球裝飾房間
System.out.println("用氣球裝飾房間, 好漂亮");
}
}
public class DecoratorPatternTest {
public static void main(String[] args) {
IHouse house = new Villa();
house.clean();
System.out.println("-------聖誕節到了, 裝飾房間-------");
IHouse decoratorHouse = new BalloonDecoratorHouse(new Villa());
decoratorHouse.clean();
}
}
使用裝飾器模式以后, 我們在不改變原有房子的基礎上進行了擴展. 節假日, 就可以使用裝飾類裝飾房間, 如果節日過去了, 我們就繼續使用原來的房子.
接下來, 我們來總結一些上面的案例.
首先, 我們有一個組件Component, 在這個組件里有一些自定義的功能. 通常這個組件Component是抽象的(接口或者抽象類)
然后, 抽象類會有一個具體的實現 ConcreteComponent, 除了實現組件Component的方法, 還可自定義方法.
第三, 我們現在想要對具體實現ConcreteComponent進行包裝, 定義一個包裝類 DecoratorComponent. 通常這個包裝類是抽象的. 包裝類也實現組件Component接口, 然后引入一個Component的具體的成員變量. 為什么要引入成員變量呢? 這個也很好理解, 我們的目標是包裝的具體類.
第四, 定義一個具體的裝飾器 DecoratorComponent, 再具體的裝飾器中, 可以增加額外的方法. 比如在之前后者之后增加一些邏輯. 具體關系圖如下:
代碼實現如下:
/**
* 抽象功能
*/
public interface Component {
void operate();
}
/**
* 具體功能實現
*/
public class ConcreteComponent implements Component{
@Override
public void operate() {
// 實現具體的邏輯
System.out.println("具體實現的邏輯");
}
}
/**
* 用來裝飾Component對象的類
*/
public abstract class DeceratorComponent implements Component{
// 指定裝飾的對象
protected Component deceratedComponent;
public DeceratorComponent(Component deceratedComponent) {
this.deceratedComponent = deceratedComponent;
}
}
/**
* 具體的裝飾類
*/
public class ConcreteDeceratorComponent extends DeceratorComponent {
public ConcreteDeceratorComponent(Component deceratedComponent) {
super(deceratedComponent);
}
@Override
public void operate() {
before();
super.deceratedComponent.operate();
after();
}
public void before(){
System.out.println("在原邏輯之前增加了邏輯");
}
public void after(){
System.out.println("在原邏輯之后增加了邏輯");
}
}
public class DeceratorTest {
public static void main(String[] args) {
Component concreteCom = new ConcreteComponent();
concreteCom.operate();
System.out.println("============");
DeceratorComponent decerator = new ConcreteDeceratorComponent(new ConcreteComponent());
decerator.operate();
}
}
運行結果:
具體實現的邏輯
============
在原邏輯之前增加了邏輯
具體實現的邏輯
在原邏輯之后增加了邏輯
設計模式的靈活應用. 假如當前具體類就只有一個. 我們就不需要定義抽象的Component了. 如何實現裝飾模式呢?
那就讓裝飾器直接繼承自原來的類就可以了:

二. 裝飾器模式的特點:
- 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
- 裝飾對象包含一個真實對象的引用(reference)
- 裝飾對象接受所有來自客戶端的請求。它把這些請求轉發給真實的對象。
- 裝飾對象可以在轉發這些請求以前或以后增加一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就可以在外部增加附加的功能。在面向對象的設計中,通常是通過繼承來實現對給定類的功能擴展。
三. 裝飾器模式的使用場景:
以下情況可以考慮使用裝飾器模式
- 需要擴展一個類的功能,或給一個類添加附加職責。
- 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
- 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關系變的不現實。
- 當不能采用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,為支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因為類定義被隱藏,或類定義不能用於生成子類。
四. 裝飾器模式的優點:
- Decorator模式與繼承關系的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
- 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行為的組合。
五. 裝飾器模式的缺點
- 這種比繼承更加靈活機動的特性,也同時意味着更加多的復雜性。
- 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很復雜。
- 裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應該重新思考你的應用架構,以及裝飾者是否合適。當然也可以改變Component接口,增加新的公開的行為,實現“半透明”的裝飾者模式。在實際項目中要做出最佳選擇。
六. 思考: 裝飾器模式使用了哪些設計模式的原則?
- 最明顯的體現就是開閉原則---對擴展開發, 對修改關閉. 當新的需求到達, 在不改變原來功能的基礎上進行擴展.
- 依賴倒置原則---依賴於抽象, 而非具體. 方便擴展, 再來一種新房子, 不用修改房子裝飾類.
- 單一職責原則: 一個類只負責一件事
