一、概念
- 繼承的缺點:類數量爆炸、設計死板以及基類加入的新功能可能並不適用於所有的子類。
- 裝飾者模式:動態地將責任附加到對象上,若要擴展功能,裝飾者提供了比繼承更有彈性的替代方案。一言以蔽之 —— 動態擴展類的行爲。
- 角色: 1、抽象組件(Component):給出一個抽象類或接口,以規範準備接收附加責任的行爲。 2、具體組件(ConcreteComponent):繼承或實現抽象組件,定義一個將要接收附加責任的類。 3、抽象裝飾者(Decorator):持有一個組件(Component)對象的實例,一般從抽象組件擴展,類型是抽象類或者接口,目的是達到"類型匹配"。 4、具體裝飾者(ConcreteDecorator):繼承或實現抽象裝飾者,負責給組件對象"貼上"附加的責任。
二、Demo 實現
TOPIC:我們要定義一些飲品,並能夠向飲品中添加一些調料,比如摩卡、糖之類的,然後能夠根據添加的調料種類動態的修改飲品的價格。
1、抽象組件
public abstract class Beverage { String description = "Unknown Beverage"; public String getDescription() { return description; } public abstract double cost(); }
這是一個抽象組件角色 —— 飲品抽象類,抽象方法 cost() 用來規範接收附加責任的行爲。
2、具體組件
public class Espresso extends Beverage { public Espresso() { description = "Espresso"; } @Override public double cost() { return 1.99; } }
public class HouseBlend extends Beverage { public HouseBlend() { description = "HouseBlend"; } @Override public double cost() { return 0.89; } }
這是兩個具體組件角色 —— 濃縮咖啡類和混合飲料類,用來接受附加責任,也就是裝飾者具體要裝飾的類!
3、抽象裝飾者
public abstract class CondimentDecorator extends Beverage { protected Beverage beverage; @Override public abstract String getDescription(); }
這是一個抽象裝飾者角色,主要的目的是爲了"類型匹配",什麼是"類型匹配"呢?不多說,看看下面的測試類就一目瞭然了!
4、具體裝飾者
現在我們想往飲品中添加摩卡,然後在飲品的價格上添加上摩卡的價格,也就是在不改變飲品類內部代碼的情況下擴展飲品類的行爲 —— 用摩卡類修飾飲品類!
public class Mocha extends CondimentDecorator { public Mocha(Beverage b) { beverage = b; } @Override public String getDescription() { return beverage.getDescription() + "+Mocha"; } @Override public double cost() { return beverage.cost() + 0.20; } }
5、測試
測試類能幫你對裝飾者模式有更清晰的認識!
public static void main(String[] args) { Beverage beverage = new Espresso(); System.out.println("濃縮咖啡:" + beverage.getDescription() + "," + beverage.cost()); Beverage beverage1 = new HouseBlend(); // 抽象裝飾者的 "類型匹配" 如下 —— 可以用同一個實例對象接收裝飾對象,以達到類行爲擴展的目的。 beverage1 = new Mocha(beverage1); beverage1 = new Mocha(beverage1); System.out.println("混合飲料+2份摩卡:" + beverage1.getDescription() + "," + beverage1.cost()); }
演示源代碼:https://github.com/JMCuixy/design-patterns
三、總結
- 設計原則:類應該對擴展開放,對修改關閉。
- 特點: 1、裝飾者和被裝飾者對象有相同的超類型。 2、你可以用一個或多個裝飾者包裝一個對象。 3、裝飾者可以在所委託的被裝飾者行爲之前或之後,加上自己的行爲,以達到特定的目的。 4、對象可以在任何時候被裝飾,所以你可以在運行時動態地、不限量的用你喜歡的裝飾者來裝飾對象。
- 優點:裝飾者模式的設計具有彈性,可以應對改變,可以接受新的功能來應對改變的需求。
- 缺點:如果每個地方都採用開放-封閉原則是一種浪費,也沒必要,還會導致代碼變得複雜且難以理解。比如會產生很多的小類。