合工大很牛很牛牛

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            14 Posts :: 1 Stories :: 37 Comments :: 0 Trackbacks

           

          一個旅館提供各種飲料(Beverage), 比如有HouseBlendDarkRoast等,每個飲料還有很多配料比如MilkSoy等。如何寫一個程序能夠方便的輸出每種飲料的價格呢?(包括飲料+配料)

           

          最笨的方法如下:也就是每種飲料,每種飲料加配料的組合,都寫一個類。

          package javaapplication33;

           

          public class Main {

              public static void main(String[] args) {

                  Beverage b=new HouseBlendWithMilk();

                  System.out.println(b.getDescription());

                  System.out.println(b.cost());

              }

          }

           

          abstract class Beverage {

              public abstract String getDescription();

              public abstract int cost();

          }

           

          class HouseBlend extends Beverage {

              @Override

              public String getDescription() {

                  return "A cup of HouseBlend";

              }

              @Override

              public int cost() {

                  return 10;

              }

          }

           

          class DarkRoast extends Beverage {

              @Override

              public String getDescription() {

                  return "A cup of DarkRoast";

              }

              @Override

              public int cost() {

                  return 8;

              }

          }

           

          class HouseBlendWithMilk extends Beverage{

              @Override

              public String getDescription() {

                  return "A cup of HouseBlend with a soap of milk.";

              }

              @Override

              public int cost() {

                 return 12;

              }

             

          }

           

          問題來了,如果飲料及其配料的排列組合有20種,30種,這很可能。那么是不是就要繼承Beverage寫上2030個類呢?這倒不是關鍵,比如萬一Milk的價格變動了,那么所有有關Milk的類都要把cost()方法重寫。。。。這才恐怖呢。原因是我們沒有把變化的部分封裝起來。

           

          OK,我們開始封裝。為方便起見,我們仍把主要的飲料HouseBlend, DarkRoast等寫成父類Beverage的子類。我們把造成變化的配料MilkSoy等把它們封裝起來,封裝到哪?封裝到父類里面去。看看下面這樣行不行:

          package javaapplication33;

           

          public class Main {

              public static void main(String[] args) {

                  Beverage b = new HouseBlend();

                  b.setMilk(true); //增加一個配料

                  System.out.println(b.cost());

                  b.setSoy(true);

                  System.out.println(b.getDescription());

                  System.out.println(b.cost());

                  b.setMilk(false);

                  System.out.println(b.cost());

                  b.setSoy(false);

                  System.out.println(b.cost());

                  System.out.println(b.getDescription());

              }

          }

           

          abstract class Beverage {

              int MILKPRICE = 2;

              int SOYPRICE = 3;

              public boolean milk;

              public boolean soy;

              int cost;

              String description;

              public void setMilk(boolean b) {

                  milk = b;

              }

              public void setSoy(boolean b) {

                  soy = b;

              }

              public int cost() {

                  cost = 0;

                  if (milk) {

                      cost += MILKPRICE;

                  }

                  if (soy) {

                      cost += SOYPRICE;

                  }

                  return cost;

              }

              public String getDescription() {

                  description = "";

                  if (milk) {

                      description += " with a soap of milk";

                  }

                  if (soy) {

                      description += " with a bottle of soy";

                  }

                  return description;

              }

          }

           

          class HouseBlend extends Beverage {

              @Override

              public String getDescription() {

                  return "a cup of HouseBlend" + super.getDescription();

              }

              @Override

              public int cost() {

                  return super.cost() + 10;

              }

          }

           

          class DarkRoast extends Beverage {

              @Override

              public String getDescription() {

                  return "a cup of DarkRoast" + super.getDescription();

              }

              @Override

              public int cost() {

                  return super.cost() + 8;

              }

          }

           

          這樣,每當我需要一個飲料加幾個配料時,僅需要new這個飲料,再set配料即可。貌似這個程序沒問題了。

           

          貌似!

          (1)      當配料的價格改動時,Beverage這個類需要改動。設計模式的原則是在提供擴展的同時,盡可能不改動原有的程序的。

          (2)       如果新增,或者減少某幾種配料,同理,Beverage仍需改動。    

          (3)       如果我們要雙份的Milk呢?

          崩潰!

           

          以上,第一種思路是把所有的排列組合都寫成子類,當一個配料變動,會導致所有相關的組合都要變動。

          第二種思路是把飲料寫成子類,配料的設置封裝到父類中去,當配料變動時,會導致原有的父類變動,并且無法同時提供兩份同樣的配料。

           

          其實當第一種思路被否定掉的時候就有一種沖動,就是把飲料和配料都寫成子類,繼承抽象的父類Beverage。這個其實很容易做到。只是主程序在調用時,比如我要一個HouseBlend配上Milk的時候,我該怎么生成呢?new一個HouseBlend,再new一個Milk,然后呢?怎么讓它打印出“A Houseblend with a cup of milk”這樣,并且算價格的時候直接兩者的cost就能疊加呢?

           

          我們想到了在讀寫文件操作時的樣子: new XXX(new XXX(new XXXXX())); 如果飲料加上配料可以這樣生成,豈不是全都解決了?

           

          package javaapplication32;

           

          public class Main {

              public static void main(String[] args) {

                  Baverage b = new Milk(new HouseBland(new Soy()));

                  System.out.println(b.getDescription());

                  System.out.println(b.getCost());

              }

          }

           

          abstract class Baverage {

              abstract String getDescription();

              abstract int getCost();

          }

           

          class HouseBland extends Baverage {

              Baverage baverage;

              HouseBland() {

              }

              HouseBland(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return "A cup of HouseBland" + baverage.getDescription();

                  }

                  else {

                      return "A cup of HouseBland";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return 10 + baverage.getCost();

                  }

                  else {

                      return 10;

                  }

              }

          }

           

          class Milk extends Baverage {

              Baverage baverage;

              Milk() {

              }

              Milk(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a soap Milk";

                  }

                  else {

                      return " And a soap Milk";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 5;

                  }

                  else {

                      return 5;

                  }

              }

          }

           

          class Soy extends Baverage {

              Baverage baverage;

              Soy() {

              }

              Soy(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a bottle of Soy";

                  }

                  else {

                      return " And a bottle of Soy";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 7;

                  }

                  else {

                      return 7;

                  }

              }

          }

           

          問題解決了?可不可以更完美呢?我們發現每一個子類里面都用了大量的判斷語句 

          if (baverage != null) {…..} else {…..}

          幾乎都是一樣的,為什么每個都要寫呢?原因是我們沒有人為的規定Baverage b = new Milk(new HouseBland(new Soy()));這個語句的順序。是“配料(new 飲料()) 還是“飲料(new 配料)”?如果我們規定一下順序的話,就有很多前面的if判斷語句不需要反復寫。

           

          這個改進看起來好像不重要,但是,在實際工作中,像HouseBlend這樣的類就像是原先寫好的類,或者幾乎長時間不會改變的類;而想配料Milk這樣的類,就如同經常改動或者新增進去的類。也就是說前者不怎么改動,而后者會經常變。那么我們就要盡量保證前者的穩定和簡單(越簡單越不易出錯)。

           

          下面的程序,我們刪除了HouseBlend中的繁雜的判斷語句,在主函數生成飲料加配料時,我們要保證“new 配料(new 配料(….(new 飲料())))的模式(先配料后飲料)。(因為一個飲料可以配多個配料而一個配料不可以配多個飲料)

          源代碼如下:

          package javaapplication32;

           

          public class Main {

              public static void main(String[] args) {

                  Baverage b = new Milk(new Soy(new HouseBlend()));

                  System.out.println(b.getDescription());

                  System.out.println(b.getCost());

              }

          }

           

          abstract class Baverage {

              abstract String getDescription();

              abstract int getCost();

          }

           

          class HouseBlend extends Baverage {

              @Override

              String getDescription() {

           

                  return "A cup of HouseBland";

              }

              @Override

              int getCost() {

                  return 10;

              }

          }

           

          class Milk extends Baverage {

              Baverage baverage;

              Milk() {

              }

              Milk(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a soap Milk";

                  }

                  else {

                      return " And a soap Milk";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 5;

                  }

                  else {

                      return 5;

                  }

              }

          }

           

          class Soy extends Baverage {

              Baverage baverage;

              Soy() {

              }

              Soy(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a bottle of Soy";

                  }

                  else {

                      return " And a bottle of Soy";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 7;

                  }

                  else {

                      return 7;

                  }

              }

          }

           

          其實我們還沒揭示Decorator pattern的核心含義:配料跟主料其實不是一回事,但是為了實現主料的新功能,又要保持主料的穩定,我們就用配料繼承主料,或者繼承主料的父函數,目的就是獲得相通的類型(type match)。

          這樣,我們可以在其他程序調用時,隨時給主料添加配料的新功能。這就是composition組合的魅力!

           

          最后,我們對上面的程序做一下最終的改進,原因是由于在類的層次設計的時候,我們沒有區分飲料和配料之間的關系,因為它們都平行的繼承了同一個抽象類。在Decorator pattern里面,往往會出現這樣的情況:就是飲料的種類很穩定,而配料的種類卻很繁雜。為了讓程序看上去更清晰(一眼就能看出誰是主,誰是配),我們用特定的一個抽象類繼承原先的父類,再讓所有的配料繼承該抽象類。

           

          package javaapplication32;

           

          public class Main {

              public static void main(String[] args) {

                  Baverage b = new Milk(new Soy(new HouseBlend()));

                  System.out.println(b.getDescription());

                  System.out.println(b.getCost());

              }

          }

           

          abstract class Baverage {

              abstract String getDescription();

              abstract int getCost();

          }

           

          class HouseBlend extends Baverage {

              @Override

              String getDescription() {

           

                  return "A cup of HouseBland";

              }

              @Override

              int getCost() {

                  return 10;

              }

          }

           

          abstract class Decorator extends Baverage {

              abstract String getDescription();

              abstract int getCost();

          }

           

          class Milk extends Decorator {

              Baverage baverage;

              Milk() {

              }

              Milk(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a soap Milk";

                  }

                  else {

                      return " And a soap Milk";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 5;

                  }

                  else {

                      return 5;

                  }

              }

          }

           

          class Soy extends Decorator {

              Baverage baverage;

              Soy() {

              }

              Soy(Baverage baverage) {

                  this.baverage = baverage;

              }

              @Override

              String getDescription() {

                  if (baverage != null) {

                      return baverage.getDescription() + " And a bottle of Soy";

                  }

                  else {

                      return " And a bottle of Soy";

                  }

              }

              @Override

              int getCost() {

                  if (baverage != null) {

                      return baverage.getCost() + 7;

                  }

                  else {

                      return 7;

                  }

              }

          }

           

          其實java API里面有很多使用Decorator Pattern的例子,比如讀寫文件:

          posted on 2008-07-04 10:59 化的了 閱讀(947) 評論(1)  編輯  收藏 所屬分類: 設計模式

          Feedback

          # re: 配件模式 Decorator Pattern 2008-07-04 13:13
          寫得很好。
          受益了。  回復  更多評論
            

          主站蜘蛛池模板: 沅陵县| 永济市| 伊金霍洛旗| 安达市| 阳城县| 长武县| 卢氏县| 孟连| 桦南县| 双桥区| 信丰县| 白银市| 黄冈市| 大竹县| 新田县| 襄垣县| 通城县| 钟祥市| 拉萨市| 鄂托克前旗| 汉寿县| 信宜市| 澄江县| 仁布县| 鲜城| 阿拉善右旗| 花莲县| 盐津县| 兴山县| 辽阳市| 手机| 贵港市| 伊春市| 杂多县| 镇坪县| 汪清县| 枣庄市| 武威市| 财经| 利辛县| 诸城市|