裝飾者模式


      裝飾者模式能夠動態地將責任附加到對象上,在擴展對象功能方面比繼承更加靈活,具體來說,裝飾者模式將行為委托給相應的包裝對象,並添加上自己的對應邏輯來實現特定的功能。裝飾者模式的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   @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 }

      接下來是配菜相關接口和類:

 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 }

      輸出如下:

炒飯 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設計模式>>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM