1. 程式人生 > >走進設計模式的世界3:為什麼我不能加兩份珍珠-裝飾者模式

走進設計模式的世界3:為什麼我不能加兩份珍珠-裝飾者模式

裝飾者模式 :

動態的將責任附加到物件上。想要拓展功能,裝飾者提供有別於繼承的另一種選擇。

解釋:裝飾者模式,雖然依賴著傳統OO模式的有一個共有基類的方式,使用了繼承。但這樣做的目的是為了保證裝飾者的型別匹配,而不是利用了繼承的行為。當我們利用裝飾者時,實在加入新的行為,並不是繼承自超類,而是由物件組合而來的。

裝飾者模式樣例演示:為什麼我不能加兩份珍珠?????

設計原則:類應該對拓展開放,對修改禁止。

炎炎夏日,李校長來到了一家新開的小成本奶茶店。奶茶店老闆開開心心的給我演示著他計算機系兒子為他寫的收銀系統。我樂呵呵的點了一杯抹茶奶綠加兩份珍珠。老闆卻告訴我只能加一份。。。。好奇的我看了下收銀系統如下。。。

/**

這是一個飲料類的基類,同樣是一個抽象類,為了控制飲料的行為。
分別存在兩個抽象方法,描述以及花費。
以及。。。冗長的配料資訊。。。。。
**/
public Abstract class Drink{
    // 飲料的描述資訊
    private String description;
    // 抹茶
    private MoCha mocha;
    // 珍珠
    private Zhenzhu zhenzhu;
    // 香草
    private Xiangcao xiangcao;
    // 布丁
    private Buding buding;
    ....
    ....
    // 獲取描述資訊
    public String getDescription();
    // 獲取花費
    public double cost();
       
    // 判斷是否加了抹茶的方法
    public boolean isMocha(){
        return mocha==null;
    }
    
    // 判斷是否加了珍珠的方法
    public boolean isZhenzhu(){
        return zhenzhu==null;
    }
    ....
    ....
}

/**

我們來看一個奶茶的實現

**/

public class NaiCha(){

    // 奶茶的構造方法
    public NaiCha(String description){
        this.description = description;
    }

    // 覆蓋基類的構造方法
    public double  cost(){
        double naichaCost = 3.75;
        if(isMocha){
            naichaCost +this.mocha.price;
        } else if(isZhenzhu){//不能加兩份珍珠的症狀就出現在這裡了。。。。
            naichaCost +this.zhenzhu.price;
        } else if......
        ......
    }

}


這樣的系統設計,讓老闆兒子對自己的程式設計基礎有了一次明顯的提升,但仍存在很多問題。

例如:如果老闆再加一種配料,他就需要對所有的類重新加一層判斷,修改及其麻煩,再一個就是加兩份珍珠的問題了。。。。

為了能喝加兩份珍珠的奶茶!我們幫主老闆用裝飾者模式做了一些修改,雖然還有提升的空間但是我們為了儘量少的改動做如下修改

/**

改寫後的飲料類

**/
public Abstract class Drink{

    // 描述
    public String descpririon();
    // 花費
    public double cost;
}


/**

奶茶

**/

public class NaiCha extends Drink{
    
    public double cost{
        return 2.33
    }

    public String description{
        return "奶茶"
    }
} 

/**

紅茶    

**/
public class Hongcha extends Drink{
    public double cost{
        return 2.11
    }

    public String descrpition{
        return "紅茶"
    }
}

......

/**
    然後就開始加料部分了
**/

// 珍珠(寫到這部分真是好雞凍。。。)
public class Zhenzhu extends Drink{
    private Drink drink;

    public Zhenzhu(Drink drink){
        this.drink = drink;
    }

    public double cost{
        return 2.11+drink.cost();
    }

    public String descripton{
        return drink.description()+"+珍珠";
    }
}

// 布丁
public class Buding extends Drink{
    private Drink drink;

    public Buding (Drink drink){
        this.drink = drink;
    }

    public double cost{
        return 2.12+drink.cost();
    }

    public String descripton{
        return drink.description()+"+布丁";
    }
}


// 仙草
public class XianCao extends Drink{
    private Drink drink;

    public XianCao(Drink drink){
        this.drink = drink;
    }

    public double cost{
        return 2.12+drink.cost();
    }

    public String descripton{
        return drink.description()+"+仙草";
    }
}


/**

   讓我們來模擬點餐吧

**/

public static void main(String[]agrs){
    // 先點一杯奶茶
    Drink drink  = new NaiCha();
    
    // 日常加點布丁
    drink = new Buding(drink);//此處程式碼對奶茶進行了包裝,變成了布丁的屬性。
    // 日常加點仙草
    drink= new XianCao(drink);//此處程式碼對奶茶+布丁做了包裝,變成了仙草的屬性。
    // 哈哈哈哈哈,加兩份珍珠,emmmmmm 包裝兩次
    drink = new Zhenzhu(drink);
    drink = new Zhenzhu(drink);
    // 這樣就完整的模擬了過程
    drink.cost();
    drink.description();
}






總結:繼承屬於拓展形式之一,但不見得是達到彈性設計的最佳方式。在我們的設計中,應該只能允許設計的類被拓展,不能允許類被修改。組合和委託可用於在執行時動態的加上新的行為。裝飾者可以在被裝飾者行為的前/後加上自己的行為,來達到特定的目的。你可以用無數個裝飾者包裝一個元件。但裝飾者會導致設計中存在過多的小物件。會讓程式變得複雜。