需求
咖啡店,客戶可能會叫一種飲料,并且指定某些調(diào)料加入進去,比如深焙咖啡,用摩卡,奶泡,牛奶作為調(diào)料,計算出總價
飲料類,被裝飾者
/**
* 飲料父類
*/
public abstract class Beverage
{
/**
* 描述飲料的字段
*/
protected String description = "Unknown Berverage";
public String getDescription()
{
return description;
}
/**
* 計算價錢的方法,必須在子類中實現(xiàn)
*/
public abstract double cost();
}
/**
* 暗烤咖啡
*/
public class DarkRoast extends Beverage
{
public DarkRoast()
{
description = "暗烤咖啡 DarkRoast"; // 描述此飲料
}
/**
* 返回此飲料的價錢
*/
@Override
public double cost()
{
return .99;
}
}
調(diào)料類,裝飾者
/**
* 調(diào)料類,也就是裝飾者
*
* 為了讓 裝飾者 能夠替代 被裝飾者 ,因此 裝飾者 繼承 被裝飾者
*/
public abstract class Condiment extends Beverage
{
/**
* 所有的裝飾者類,都必須重新實現(xiàn)此方法,因為必須有不同的名稱
*/
@Override
public abstract String getDescription();
}
/**
* 摩卡調(diào)料
*/
public class Mocha extends Condiment
{
private Beverage beverage; // 用來記住 被裝飾者 ,也就是此調(diào)料要加在哪個飲料上
/**
* 想辦法把被裝飾者記錄在變量中
*/
public Mocha(Beverage beverage)
{
this.beverage = beverage;
}
/**
* 把被裝飾者和裝飾者的名稱組合起來,比如:混合咖啡,摩卡
*/
@Override
public String getDescription()
{
return beverage.getDescription() + ", 摩卡調(diào)料 Mocha";
}
/**
* Mocha自身的價錢+飲料的價錢
*/
@Override
public double cost()
{
return 0.20 + beverage.cost();
}
}
/**
* 豆奶調(diào)料
*/
public class Soy extends Condiment
{
private Beverage beverage;
public Soy(Beverage beverage)
{
this.beverage = beverage;
}
@Override
public String getDescription()
{
return beverage.getDescription() + ", 豆奶調(diào)料 Soy";
}
@Override
public double cost()
{
return .15 + beverage.cost();
}
}
測試
// 點一杯飲料
Beverage beverage2 = new DarkRoast();
// 用調(diào)料去裝飾飲料
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
// 算出總價錢
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
疑問
- 以上全用了繼承,這里是利用繼承達到類型匹配的目的,并不是獲得行為。方便更有彈性的混合匹配
- 每當(dāng)需要新的調(diào)料,甚至是新的飲料,都可以新增并方便的加入
- 并非一定要用接口,如果抽象類用的好好的,并且已經(jīng)滿足需求,就不用再去修改他
- 會導(dǎo)致有很多的小類
Java 中應(yīng)用到裝飾者模式的地方
Java I/O
設(shè)計原則 5
類應(yīng)該對擴展開放,對修改關(guān)閉,在不修改現(xiàn)有代碼的情況下,就搭配新的行為
裝飾者模式定義:
動態(tài)的將責(zé)任附加到對象上,對于擴展的話,比繼承更有彈性。