裝飾者模式能夠動態地將責任附加到物件上,在擴展物件功能方面比繼承更加靈活,具體來說,裝飾者模式將行為委托給相應的包裝物件,并添加上自己的對應邏輯來實作特定的功能,裝飾者模式的UML圖如下:

首先需要有被裝飾的組件介面和具體組件,然后有裝飾者物件,由于裝飾者物件需要能夠代替組件,所以要繼承組件介面,并組合組件物件來完成委托任務,
下面以一個簡單的快餐店為例子來介紹裝飾者模式的用法,快餐店會有炒面、炒飯這些快餐,可以額外附加雞蛋、火腿、培根這些配菜,當然加配菜需要額外加錢,每個配菜的價錢通常不太一樣,那么計算總價就會顯得比較麻煩,舉個極端的例子,某個顧客一下子加了20中配菜,要計算總共需要多少錢估計還得拿個計算器出來算一會兒,這種情況下裝飾者模式就有了用武之地,
按照上面的UML類圖,先定義組件介面(快餐)和具體組件(炒飯、炒面),
1 //快餐介面 2 public interface FastFood { 3 float getCost(); //獲取價格 4 String getDescription(); //獲取描述 5 } 6 7 //炒飯 8 public class FriedRice implements FastFood{ 9 private float price = 5;10 String description = "炒飯";11 @Override12 public float getCost() {13 return this.price;14 }15 16 @Override17 public String getDescription() {18 return this.description;19 }20 }21 22 //炒面23 public class FriedNoodles implements FastFood{24 private float price = 5;25 String description = "炒面";26 @Override27 public float getCost() {28 return this.price;29 }30 31 @Override32 public String getDescription() {33 return this.description;34 }35 }
接下來是配菜相關介面和類:
1 //配菜 2 public interface Garnish extends FastFood{ 3 String getDescription(); 4 } 5 6 //雞蛋 7 public class Egg implements Garnish{ 8 float price = 1.5f; 9 String description = "雞蛋";10 private FastFood fastFood;11 12 public Egg(FastFood fastFood){13 this.fastFood = fastFood;14 }15 16 @Override17 public float getCost() {18 return this.price + fastFood.getCost();19 }20 21 @Override22 public String getDescription() {23 return this.description + fastFood.getDescription();24 }25 }26 27 //培根28 public class Bacon implements Garnish{29 private float price = 2f;30 private String description = "培根";31 private FastFood fastFood;32 33 public Bacon(FastFood fastFood){34 this.fastFood = fastFood;35 }36 37 @Override38 public float getCost() {39 return this.price + fastFood.getCost();40 }41 42 @Override43 public String getDescription() {44 return this.description + fastFood.getDescription();45 }46 } 47 48 //火腿49 public class Ham implements Garnish{50 float price = 1f;51 String description = "火腿";52 private FastFood fastFood;53 54 public Ham(FastFood fastFood){55 this.fastFood = fastFood;56 }57 58 @Override59 public float getCost() {60 return this.price + fastFood.getCost();61 }62 63 @Override64 public String getDescription() {65 return this.description + fastFood.getDescription();66 }67 }
接下來就可以點幾份快餐測驗一下代碼了:
1 public class DecoratorPatternTest { 2 public static void main(String[] args){ 3 FastFood friedRice = new FriedRice(); 4 System.out.println(friedRice.getDescription() + " " + friedRice.getCost() + "元"); 5 FastFood friedNoodles = new FriedNoodles(); 6 friedNoodles = new Egg(friedNoodles); 7 friedNoodles = new Ham(friedNoodles); 8 System.out.println(friedNoodles.getDescription() + " " + friedNoodles.getCost() + "元"); 9 friedRice = new Bacon(friedRice);10 System.out.println(friedRice.getDescription() + " " + friedRice.getCost() + "元");11 }12 }
輸出如下:
炒飯 5.0元火腿雞蛋炒面 7.5元培根炒飯 7.0元
現在來回顧一下上面的類結構,我們在點培根炒飯時,先創建一個炒飯物件,然后用培根物件把炒飯包裝了一下,當計算價格以及輸出描述時,我們的做法是獲取裝飾者(培根)物件的價格和描述,同時需要獲取組件(炒飯)的價格我描述,我們將這項任務委托給組件來完成,火腿雞蛋炒面也是同樣的道理,裝飾者繼承組件,使得裝飾者可以包裝任意的具體組件,同樣也可以包裝裝飾者;同時,裝飾者也可以加入自己的邏輯,給組件增添不一樣的行為,例如這里在技術價格以及獲取描述時,除了回傳裝飾者自己的屬性,還增加了回傳組件屬性的邏輯,
其實裝飾者模式在Java API中使用的很多,舉個很簡單的例子,當我們需要讀取檔案時,很可能會寫出下面的代碼:
1 BufferedReader bf = new BufferedReader(new FileReader(new File("./test.txt")));
其中BufferedReader和FileReader就充當了裝飾者的作用,而File則是被裝飾的組件,還有一些其他的API也用到了裝飾者模式,需要大家自己去摸索了,
參考文獻:
<<Head First設計模式>>
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/42101.html
標籤:設計模式
上一篇:設計模式-結構型-享元模式
下一篇:設計模式-結構型-組合模式
