我們都知道,可以使用兩種方式給一個類或者對象添加行為。
一是使用繼承。繼承是給一個類添加行為的比較有效的途徑。通過使用繼承,可以使得子類在擁有自身方法的同時,還可以擁有父類的方法。但是使用繼承是靜態的,在編譯的時候就已經決定了子類的行為,我們不便於控制增加行為的方式和時機。
二是使用關聯。組合即將一個對象嵌入到另一個對象中,由另一個對象來決定是否引用該對象來擴展自己的行為。這是一種動態的方式,我們可以在應用程序中動態的控制。
與繼承相比,關聯關系的優勢就在於不會破壞類的封裝性,且具有較好的松耦合性,可以使系統更加容易維護。但是它的缺點就在於要創建比繼承更多的對象。
一、基本定義
裝飾者模式,動態地將責任附加到對象上。若要擴展功能,裝飾者提供了比繼承更加有彈性的替代方案。
二、模式結構
裝飾者模式UML結構圖。
Component: 抽象構件。是定義一個對象接口,可以給這些對象動態地添加職責。
ConcreteComponent:具體構件。是定義了一個具體的對象,也可以給這個對象添加一些職責。
Decorator: 抽象裝飾類。是裝飾抽象類,繼承了Component,從外類來擴展Component類的功能,但對於Component來說,是無需知道Decorator存在的。
ConcreteDecorator:具體裝飾類,起到給Component添加職責的功能。
三、實現裝飾者模式
結構圖如下:
裝飾者模式提供了一個比較好的解決方案。
編碼實現:
Component Beverage.java
1 public abstract class Beverage { 2 protected String description = "Unknown Beverage"; 3 4 public String getDescription() { 5 return description; 6 } 7 8 public abstract double cost(); 9 }
四個組件:HouseBlend.java
1 public class HouseBlend extends Beverage { 2 3 public HouseBlend(){ 4 description = "HouseBlend"; 5 } 6 7 @Override 8 public double cost() { 9 return 0.89; 10 } 11 12 }
DarkRoast.java
1 public class DarkRoast extends Beverage { 2 public DarkRoast(){ 3 description = "DarkRoast"; 4 } 5 @Override 6 public double cost() { 7 return 1.05; 8 } 9 10 }
Espresso.java
1 public class DarkRoast extends Beverage { 2 public DarkRoast(){ 3 description = "DarkRoast"; 4 } 5 @Override 6 public double cost() { 7 return 1.05; 8 } 9 10 }
Decat.java
1 public class Decat extends Beverage { 2 public Decat(){ 3 description = "Decat"; 4 } 5 6 @Override 7 public double cost() { 8 return 0.99; 9 } 10 11 }
CondimentDecorator.java
1 public abstract class CondimentDecorator extends Beverage{ 2 public abstract String getDescription(); 3 }
Milk.java
1 public class Milk extends CondimentDecorator { 2 Beverage beverage; 3 4 public Milk(Beverage beverage){ 5 this.beverage = beverage; 6 } 7 8 @Override 9 public String getDescription() { 10 return beverage.getDescription() + " , Milk"; 11 } 12 13 @Override 14 public double cost() { 15 return beverage.cost() + 0.3; 16 } 17 }
Mocha.java
1 public class Mocha extends CondimentDecorator { 2 Beverage beverage; 3 public Mocha(Beverage beverage){ 4 this.beverage = beverage; 5 } 6 7 @Override 8 public String getDescription() { 9 return beverage.getDescription() + " , Mocha"; 10 } 11 12 @Override 13 public double cost() { 14 return beverage.cost() + 0.20; 15 } 16 17 }
Soy.java
1 public class Soy extends CondimentDecorator{ 2 Beverage beverage; 3 public Soy(Beverage beverage) { 4 this.beverage = beverage; 5 } 6 @Override 7 public String getDescription() { 8 return beverage.getDescription() + " , Soy"; 9 } 10 11 @Override 12 public double cost() { 13 return beverage.cost() + 0.10; 14 } 15 16 }
Whip.java
1 public class Whip extends CondimentDecorator { 2 Beverage beverage; 3 public Whip(Beverage beverage){ 4 this.beverage = beverage; 5 } 6 @Override 7 public String getDescription() { 8 return beverage.getDescription() + " , Whip"; 9 } 10 11 @Override 12 public double cost() { 13 return beverage.cost() + 0.20; 14 } 15 16 }
測試程序
1 public class StarbuzzCoffee { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) { 7 Beverage beverage = new Espresso(); 8 System.out.println(beverage.getDescription() + " $" + beverage.cost()); 9 10 Beverage beverage2 = new DarkRoast(); 11 beverage2 = new Mocha(beverage2); 12 beverage2 = new Mocha(beverage2); 13 beverage2 = new Whip(beverage2); 14 System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); 15 } 16 17 }
運行結果
優
1、裝飾者模式可以提供比繼承更多的靈活性
2、可以通過一種動態的方式來擴展一個對象的功能,在運行時選擇不同的裝飾器,從而實現不同的行為。
3、通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。可以使用多個具體裝飾類來裝飾同一對象,得到功能更為強大的對象。
4、具體構件類與具體裝飾類可以獨立變化,用戶可以根據需要增加新的具體構件類和具體裝飾類,在使用時再對其進行組合,原有代碼無須改變,符合“開閉原則”。
缺點
1、會產生很多的小對象,增加了系統的復雜性
2、這種比繼承更加靈活機動的特性,也同時意味着裝飾模式比繼承更加易於出錯,排錯也很困難,對於多次裝飾的對象,調試時尋找錯誤可能需要逐級排查,較為煩瑣。
五、模式的適用場景
1、在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。2、需要動態地給一個對象增加功能,這些功能也可以動態地被撤銷。 當不能采用繼承的方式對系統進行擴充或者采用繼承不利於系統擴展和維護時。
六、總結nbsp; 1、 組合和委托可以在運行時動態的添加新的行為,而繼承是靜態的,在系統編譯時就已經決定了對象的行為。
2、裝飾者模式意味着一群裝飾者類,這些類用來包裝具體組件
3、裝飾者可以在被裝飾者的行為前面或者后面加上自己的行為,甚至可以將被裝飾者的行為整個取代掉,從而達到特定的目的。
4、可以用多個裝飾者包裝一個組件。
5、裝飾者一般對於組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。
6、裝飾者會導致設計中出現許多的小對象,如果過度的使用,會讓系統變得更加復雜。
7、裝飾者和被裝飾者對象有相同的超類型。