天天看點

從咖啡談裝飾模式

問題描述

.他們的咖啡有不同的品牌,有latte(拿鐵),卡布奇諾(cappuccino)等等當然以後肯定會改變,同時各種咖啡都可以加上milk(牛奶),soy(豆漿),sugar(糖)等配料,當然配料也會改變(一杯咖啡可以加兩分牛奶).還有咖啡有大杯小杯.

版本一

一個coffee父類,各種品牌的開發繼承之. 語言是蒼白的 看代碼!

public class Coffee {
    private String description;      //品牌描述
    private String size;            
    private int milk;
    private int sugar;
    
    public Coffee(){                 //構造函數
        description="unknow coffee";
        size="small";
    }
    public Coffee(int milk,int sugar,String size,String brand){
        this.milk=milk;
        this.sugar=sugar;
        this.size=size;
        description=brand;
    }
    public String getDescription(){
         return description+"  "+size+"杯咖啡   milk:"+milk+"份    sugar:"+sugar+"份";        

    }
    public  double cost(){
        if(size.equals("small"))                   //小杯大杯分開
            return milk*1.1+sugar*1.2;
        if(size.equals("jourm"))
            return milk*1.2+sugar*1.4;
        return 0;
    }
}      

然後就是一個拿鐵(iatte)繼承了coffee 别的品牌都一樣的

public class iatte1 extends Coffee{
  public double cost(){
    return super.cost()+2.5;
  }
  
  public iatte1(int milk,int sugar,String size) {
    super(milk, sugar, size, "iatte");
  }
}      

再看測試類

import condiment.*;
import base.Beverage;
import brand.*;

public class test {
  public static void main(String[] args){
      Coffee aCoffee=new iatte1(2,3,"small");
      System.out.println(aCoffee.getDescription());
            System.out.println(aCoffee.cost());
  }
}      

測試結果

iatte  small杯咖啡   milk:2份    sugar:3份

8.3

大家可以看出來 關鍵的cost()計算和getdescription 都是在父類中完成的.

這樣看起來還不錯 不過真的不錯嗎?

幾個問題

1:一旦我要更改某一種調料的價格怎麼辦?

2:我要新增一種調料怎麼辦?

3:如果有了新的飲料,如茶,問題是茶不會加奶呀 怎麼辦?

分析這個三個問題

其中1,2違反了開閉原則.第三個的設計也不合适

.

此時我們看看 裝飾模式是怎麼解決這個問題的.

裝飾模式版本

我們建立一個抽象類beverage.

package base;
public abstract class Beverage {
public String description="unknow coffee";
public String size="unknow size";
public void setDescription(String description) {
this.description = description;
}

public String getDescription() {
return description;
}

    public abstract double cost();
}      

讓所有的咖啡品牌都繼承beverage.

package brand;
import base.Beverage;


public class latte extends Beverage{
    public double cost() {
        if(size.endsWith("jorum"))
            return 1.5;
        else if(size.endsWith("small"))
            return 1.2;
        return 1.2;
            
    }
    public latte(){
        size="jorum";
        description="latte jorum coffee";
    }
    public latte(String size2){
        size=size2;
        description="latte "+size+" coffee";
    }
}      

其他品牌類似,不在贅述.

   再建立一個所有調料(milk.soy等等)的基類condiment,并讓它繼承beverage.

package condiment;
import base.Beverage;


public abstract class condiment extends Beverage {
  public abstract String getDescription();
}      

 然後所有的配料繼承condiment.

package condiment;
import base.Beverage;


public class sugar extends condiment {

  Beverage beverage;
  public sugar(Beverage beverage2){
    beverage=beverage2;
  }
  
  public String getDescription() {
    System.out.print("suga 的形容  \n");
    return beverage.getDescription()+" ,sogar";
  }

  public double cost() {
    return beverage.cost()+2.4;
  }
}      

  其他配料如milk等似,不在贅述.

uml圖如下(取自head first design pattern )

從咖啡談裝飾模式

   看看測試類.

import condiment.*;
import base.Beverage;
import brand.*;

public class test {
  public static void main(String[] args){
    System.out.println("第一杯");
    Beverage aBeverage=new latte();
    System.out.println(aBeverage.getDescription());
    System.out.println(aBeverage.cost());
    System.out.println(); 
    
    System.out.println("第二杯");    
    Beverage bBeverage=new cappuccino("small");
    bBeverage=new milk(bBeverage);
    bBeverage=new milk(bBeverage);
    bBeverage=new sugar(bBeverage);
    System.out.println(bBeverage.getDescription());
    System.out.println(bBeverage.cost());

  }
}      

大家看看,在第二杯的時候,我們給咖啡加糖,加牛奶就new一個牛奶,new一個糖然後加進去很形象不是嗎?

測試結果

第一杯

latte jorum coffee

1.5

第二杯

suga 的形容  

milk 的形容

milk 的形容

cappuccino small coffee,milk,milk ,sogar

6.1

這裡有一個問題,這個小杯的含兩份牛奶一份糖的cappuccino的價格到底是怎麼算出來的呢?

 public double cost() {

        return beverage.cost()+2.4;

    }

這是糖的cost 。

看咱們的測試代碼。Cappuccino最“外邊”包的是糖,當我們調用bBeverage.cost()的時候,其實就是用糖的cost。Ok,看上面糖的cost,2.4+beverage.cout()這句代碼裡的beverage指的是誰呢?

bBeverage=new milk(bBeverage); 

bBeverage=new milk(bBeverage); 

bBeverage=new sugar(bBeverage);

很顯然是牛奶。是以再調用牛奶的cost!繼續往上走,是以的問題都解決了。其實我建議朋友們再看這部分的時候最好開啟debug模式。親自一步一步調試一下就解決問題了。

下圖來自head first 裡面的咖啡例子

從咖啡談裝飾模式

 whip,mocha都是配料和本例子中的sugar.milk是一回事.darkroast是咖啡的品牌 相當于本例中的卡布奇諾,拿鐵.

在javaio中的應用

參考資料

<Head First Design Pattern>>