在這次分享之前,我們先來分享一個設計原則------開放-關閉原則。其實在第一篇文章分享策略模式的時候已經使用過了。大家還記得那個android的圖片緩存嗎?大家發現,如果我要新增加一種緩存機制,只需要增加一個類(這個類實現ImageCache接口即可),然後然後通過setImageCache()方法將該類對象賦值給ImageLoader的ImageCache對象,就可以使用了。大家有沒有發現?我在新增這個緩存機制的過程中只是新增了一個類,並沒有對原有的代碼進行修改,這就是遵循了開放-關閉原則。
開放-關閉原則,我們的目標是允許類容易擴展,在不修改現有代碼的情況下,就可搭配新的行爲。如能實現這樣的目標,有什麼好處呢?這樣的設計具有彈性,可以應對改變,可以接受新的功能類應對改變的需求。(引用《Head First設計模式》)。
我這次分享的裝飾着模式是完全遵循開放-關閉原則的。
用《Head First設計模式》中的例子來描述吧。
需求是這樣的?我們要爲一個咖啡店設計一個訂單系統。該咖啡店有幾種(HouseBlend、DarkRoast、Decaf,Espresso)原汁原味的咖啡,都是原味的。然後顧客購買咖啡的時候,可以指定添加調料,調料有蒸奶(Steamed Milk)、豆漿(Soy)、摩卡(Mocha)和覆蓋奶泡。
添加調料可以按照客戶的要求添加多種,當然也可以不添加。然後計算出價錢(四種原味的咖啡要錢,調料也要錢)
大家可以想想你們的餓實現。下面我來分享一個裝飾者模式的實現。
如果顧客想要摩卡和奶泡深焙咖啡。
1. 拿一個深焙咖啡(DarkRoast)對象
2. 以摩卡(Mocha)對象裝飾它
3. 以奶泡(Whip)對象裝飾它
4. 調用cost()方法,並依賴委託delegate將調料的價錢加上去。
《Head First設計模式》中的圖。
首先畫出UML建模圖。
根據uml我們知道所有的主體或者裝飾者都要間接或直接實現相同的接口,這也是裝飾者模式所要求的。
首先來實現飲料的接口Bevarage,有最基本的獲取描述和計算價錢的方法
/**
* 飲料和調料的公共的接口
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:31
*/
public interface Beverage {
public double cost();
public String getDescription();
}
基於飲料的接口,我們實現四種飲料
/**
* DarkRoast原味飲料
* 該設計模式中爲主體
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:38
*/
public class DarkRoast implements Beverage {
private String des;
private double price;
public DarkRoast(){
des="Dark Roase";
price=1.1;
}
@Override
public double cost(){
return price;
}
@Override
public String getDescription(){
return des;
}
}
/**
* * Decaf原味飲料
* 該設計模式中爲主體
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:39
*/
public class Decaf implements Beverage {
private String des;
private double price;
public Decaf(){
des="Decaf";
price=1.15;
}
@Override
public double cost(){
return price;
}
@Override
public String getDescription(){
return des;
}
}
/**
* * Espresso原味飲料
* 該設計模式中爲主體
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:41
*/
public class Espresso implements Beverage {
private String des;
private double price;
public Espresso(){
des="Espresso";
price=1.2;
}
@Override
public double cost(){
return price;
}
@Override
public String getDescription(){
return des;
}
}
/**
*HouseBlend原味飲料
* 該設計模式中爲主體
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:36
*/
public class HouseBlend implements Beverage {
private String des;
private double price;
public HouseBlend(){
des="HouseBlend";
price=1.3;
}
@Override
public double cost(){
return price;
}
@Override
public String getDescription(){
return des;
}
}
}
通過前面的兩個圖,我們知道調料作爲裝飾者,在類中要有一個Beverage飲料接口的一個對象,用來指向前面最新飲料的對象,這樣在算錢的時候,可以先計算出前面的,然後在加上現在增加的。獲取描述也是一樣,先獲取前面的描述再加上現在的描述。那首先我們來實現調料的抽象類。我們知道每個調料類都有Beverage接口指向前面的飲料,那我們可以直接在抽象類中寫這個Beverage對象,然後在各自的調料類中賦值便可以了。(當然price價錢和des描述也是可以這樣做的。)
調料類接口:
/**
* 調料的接口
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:42
*/
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
@Override
public abstract double cost();
@Override
public abstract String getDescription();
}
下面是各種調料。
/**
* 調料
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:43
*/
public class Milk extends CondimentDecorator {
private String des;
private double price;
/**
*
* @param beverage
*/
public Milk(Beverage beverage){
this.beverage = beverage;
des="Milk";
price = 0.5;
}
@Override
public double cost(){
return beverage.cost()+price;//前面的飲料的價錢,再加上當前增加的
}
@Override
public String getDescription(){
return beverage.getDescription()+" "+des;
}
}
/**
* 調料
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:44
*/
public class Mocha extends CondimentDecorator {
private String des;
private double price;
/**
*
* @param beverage
*/
public Mocha(Beverage beverage){
this.beverage = beverage;
des="Mocha";
price=0.6;
}
@Override
public double cost(){
return beverage.cost()+price;
}
@Override
public String getDescription(){
return beverage.getDescription()+" "+des;
}
}
* @created 06-11月-2016 10:41:45
*/
public class Soy extends CondimentDecorator {
private String des;
private double price;
/**
*
* @param beverage
*/
public Soy(Beverage beverage){
this.beverage = beverage;
des="Soy";
price=0.4;
}
@Override
public double cost(){
return beverage.cost()+price;
}
@Override
public String getDescription(){
return beverage.getDescription()+" "+des;
}
}
/**
* 調料
* @author wangpeiyu
* @version 1.0
* @created 06-11月-2016 10:41:47
*/
public class Whip extends CondimentDecorator {
private String des;
private double price;
/**
*
* @param beverage
*/
public Whip(Beverage beverage){
this.beverage = beverage;
des="Whip";
price=0.45;
}
@Override
public double cost(){
return beverage.cost()+price;
}
@Override
public String getDescription(){
return beverage.getDescription()+" "+des;
}
}
下面來寫一個測試的例子。
public class main {
public static void main(String[] args) {
// TODO Auto-generated method stub
//新建一種飲料
Decaf beverage = new Decaf();
//原味的錢
System.out.println(beverage.getDescription()+" $"+beverage.cost());
//添加了牛奶調料,將主體傳遞進去
Beverage beverage2 = new Milk(beverage);
//添加Mocha調料,這時候將前面的最新的飲料傳遞進去
beverage2 = new Mocha(beverage2);
//添加Whip飲料,又將最新的飲料傳遞進去
beverage2 = new Whip(beverage2);
//查看當前飲料的價錢
System.out.println(beverage2.getDescription()+" $"+beverage2.cost());
}
}
結果是:
這樣就實現了客戶想加什麼調料,直接新建一個給調料的類,並將之前的飲料傳給調料裏面的beverage對象即可。