合工大很牛很牛牛

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            14 Posts :: 1 Stories :: 37 Comments :: 0 Trackbacks

          常用鏈接

          留言簿(1)

          我參與的團(tuán)隊(duì)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

           

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

           

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

          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;

              }

             

          }

           

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

           

          OK,我們開(kāi)始封裝。為方便起見(jiàn),我們?nèi)园阎饕娘嬃?/span>HouseBlend, DarkRoast等寫成父類Beverage的子類。我們把造成變化的配料MilkSoy等把它們封裝起來(lái),封裝到哪?封裝到父類里面去。看看下面這樣行不行:

          package javaapplication33;

           

          public class Main {

              public static void main(String[] args) {

                  Beverage b = new HouseBlend();

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

                  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;

              }

          }

           

          這樣,每當(dāng)我需要一個(gè)飲料加幾個(gè)配料時(shí),僅需要new這個(gè)飲料,再set配料即可。貌似這個(gè)程序沒(méi)問(wèn)題了。

           

          貌似!

          (1)      當(dāng)配料的價(jià)格改動(dòng)時(shí),Beverage這個(gè)類需要改動(dòng)。設(shè)計(jì)模式的原則是在提供擴(kuò)展的同時(shí),盡可能不改動(dòng)原有的程序的。

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

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

          崩潰!

           

          以上,第一種思路是把所有的排列組合都寫成子類,當(dāng)一個(gè)配料變動(dòng),會(huì)導(dǎo)致所有相關(guān)的組合都要變動(dòng)。

          第二種思路是把飲料寫成子類,配料的設(shè)置封裝到父類中去,當(dāng)配料變動(dòng)時(shí),會(huì)導(dǎo)致原有的父類變動(dòng),并且無(wú)法同時(shí)提供兩份同樣的配料。

           

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

           

          我們想到了在讀寫文件操作時(shí)的樣子: 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;

                  }

              }

          }

           

          問(wèn)題解決了?可不可以更完美呢?我們發(fā)現(xiàn)每一個(gè)子類里面都用了大量的判斷語(yǔ)句 

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

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

           

          這個(gè)改進(jìn)看起來(lái)好像不重要,但是,在實(shí)際工作中,像HouseBlend這樣的類就像是原先寫好的類,或者幾乎長(zhǎng)時(shí)間不會(huì)改變的類;而想配料Milk這樣的類,就如同經(jīng)常改動(dòng)或者新增進(jìn)去的類。也就是說(shuō)前者不怎么改動(dòng),而后者會(huì)經(jīng)常變。那么我們就要盡量保證前者的穩(wěn)定和簡(jiǎn)單(越簡(jiǎn)單越不易出錯(cuò))。

           

          下面的程序,我們刪除了HouseBlend中的繁雜的判斷語(yǔ)句,在主函數(shù)生成飲料加配料時(shí),我們要保證“new 配料(new 配料(….(new 飲料())))的模式(先配料后飲料)。(因?yàn)橐粋€(gè)飲料可以配多個(gè)配料而一個(gè)配料不可以配多個(gè)飲料)

          源代碼如下:

          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;

                  }

              }

          }

           

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

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

           

          最后,我們對(duì)上面的程序做一下最終的改進(jìn),原因是由于在類的層次設(shè)計(jì)的時(shí)候,我們沒(méi)有區(qū)分飲料和配料之間的關(guān)系,因?yàn)樗鼈兌计叫械睦^承了同一個(gè)抽象類。在Decorator pattern里面,往往會(huì)出現(xiàn)這樣的情況:就是飲料的種類很穩(wěn)定,而配料的種類卻很繁雜。為了讓程序看上去更清晰(一眼就能看出誰(shuí)是主,誰(shuí)是配),我們用特定的一個(gè)抽象類繼承原先的父類,再讓所有的配料繼承該抽象類。

           

          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;

                  }

              }

          }

           

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

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

          Feedback

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

          主站蜘蛛池模板: 灵石县| 涟源市| 秦皇岛市| 萝北县| 新巴尔虎左旗| 济阳县| 金秀| 灵山县| 迁西县| 六盘水市| 义乌市| 兴化市| 绿春县| 温泉县| 区。| 巴彦淖尔市| 阜康市| 伽师县| 砚山县| 申扎县| 万源市| 方城县| 望奎县| 杨浦区| 四子王旗| 图们市| 襄垣县| 隆德县| 永春县| 涿鹿县| 崇信县| 岳阳市| 台湾省| 会东县| 巩留县| 高平市| 社旗县| 江安县| 德钦县| 建湖县| 伊吾县|