裝飾者模式可以動態地將責任附加到對象上,在擴展對象功能方面比繼承更加靈活,具體來講,裝飾者模式將行爲委託給相應的包裝對象,並添加上本身的對應邏輯來實現特定的功能。裝飾者模式的UML圖以下:設計模式
首先須要有被裝飾的組件接口和具體組件,而後有裝飾者對象,因爲裝飾者對象須要可以代替組件,因此要繼承組件接口,並組合組件對象來完成委託任務。ide
下面以一個簡單的快餐店爲例子來介紹裝飾者模式的用法。快餐店會有炒麪、炒飯這些快餐,能夠額外附加雞蛋、火腿、培根這些配菜,固然加配菜須要額外加錢,每一個配菜的價錢一般不太同樣,那麼計算總價就會顯得比較麻煩。舉個極端的例子,某個顧客一會兒加了20中配菜,要計算總共須要多少錢估計還得拿個計算器出來算一下子,這種狀況下裝飾者模式就有了用武之地。測試
按照上面的UML類圖,先定義組件接口(快餐)和具體組件(炒飯、炒麪)。this
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 @Override 12 public float getCost() { 13 return this.price; 14 } 15 16 @Override 17 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 @Override 27 public float getCost() { 28 return this.price; 29 } 30 31 @Override 32 public String getDescription() { 33 return this.description; 34 } 35 }
接下來是配菜相關接口和類:spa
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 @Override 17 public float getCost() { 18 return this.price + fastFood.getCost(); 19 } 20 21 @Override 22 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 @Override 38 public float getCost() { 39 return this.price + fastFood.getCost(); 40 } 41 42 @Override 43 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 @Override 59 public float getCost() { 60 return this.price + fastFood.getCost(); 61 } 62 63 @Override 64 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 }
輸出以下:code
炒飯 5.0元 火腿雞蛋炒麪 7.5元 培根炒飯 7.0元
如今來回顧一下上面的類結構,咱們在點培根炒飯時,先建立一個炒飯對象,而後用培根對象把炒飯包裝了一下,當計算價格以及輸出描述時,咱們的作法是獲取裝飾者(培根)對象的價格和描述,同時須要獲取組件(炒飯)的價格我描述,咱們將這項任務委託給組件來完成,火腿雞蛋炒麪也是一樣的道理。裝飾者繼承組件,使得裝飾者能夠包裝任意的具體組件,一樣也能夠包裝裝飾者;同時,裝飾者也能夠加入本身的邏輯,給組件增添不同的行爲,例如這裏在技術價格以及獲取描述時,除了返回裝飾者本身的屬性,還增長了返回組件屬性的邏輯。對象
其實裝飾者模式在Java API中使用的不少,舉個很簡單的例子,當咱們須要讀取文件時,極可能會寫出下面的代碼:blog
1 BufferedReader bf = new BufferedReader(new FileReader(new File("./test.txt")));
其中BufferedReader和FileReader就充當了裝飾者的做用,而File則是被裝飾的組件,還有一些其餘的API也用到了裝飾者模式,須要你們本身去摸索了。繼承
參考文獻:
<<Head First設計模式>>