基本定义
给对象赋予超能力
动态的将责任附加到对象上。想要提供功能,装饰者提供有别于继承的另一种选择。在不修改底层代码的情况下,给对象赋予新的职责
入门
孙悟空有72般变化,他的每一种变化带来一种附加的本领。他变成鱼儿时,就可以到水里游泳;他变成雀儿时,就可以到天上飞。而在二郎神的眼里,他永远是那只猢狲
- 在装饰模式中各个角色有
- 抽象构建(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色:定义一个附加责任的类。
- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
- 具体装饰角色(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。
案例
问题 星巴兹(starbuzz)是以速度扩张最快而闻名的咖啡连锁店,如果你在街角看到他的店,在对面的街上肯定还会看到另一家。因为扩张实在太快,他们准备更新订单系统,以合乎他们的饮料供应要求。下面使他们原先的设计
不合理原因
- 调整价格会使我们更改现有的代码
- 一旦出现新饮料,我们就需要加新的方法,并改变超类中的cost()方法
- 开发新饮料,对这些饮料而言可能并不合适,例如(冰茶)子类仍将继承hashWhip()(加奶泡)
- 外衣顾客想要双倍摩卡咖啡的话,将无法实现
以装饰者构造饮料订单
- 以DarkRoast对象开始
- 顾客想要摩卡(Mocha),所以建立一个Mocha对象,并用它将DarkRoast对象(wrap)起来
- 顾客也想要奶泡(Whip),所以需要建立一个Whip装饰者,并用它将Mocha对象包裹起来。别忘了,DarkRoast继承自Beverage,且有一个coast()方法,用来计算饮料价格
- 现在为顾客算钱。通过调用最外圈的装饰者(Whip)的cost()就可以了办得到。Whip的cost()会先委托它装饰的对象(也就是Mocha)计算出价格,然后在加上奶泡的价格
上述所讲总结
将饮料当做大圣本身,调料当做大圣化生。每一次加入调料相遇于饮料增加一种功能。如大圣化为鸟被赋予飞的能力,化为鱼拥有潜水的本领…但是不管怎么变,猴子在二郎神的眼里还是那个猢狲。饮料加了配料还是饮料
- 装饰者和被装饰者对象拥有相同的超类型
- 可以用一个或者多个装饰者包装一个对象
- 根据里式替换原则,任何原始对象都可以被装饰过的对象代替他
- 装饰者可以在所委托被装饰者的行为之前或者与/或之后,加上自己的行为,已达到特定的目的
- 虽然Decorator不一定是抽象类,但由于他的功能是抽象一个抽象角色,因此也常常称他为抽象装饰
基于装饰者模式设计
Beverage(饮料)类
// 该类为抽象类
public abstract class Beverage {
String description = "Unknown Beverage";
public String getDescription() {
return description;
}
public abstract double cost();
}
实现Condiment(调料)抽象类
public abstract class CondimentDecorator extends Beverage {
// 所有的调料都必须重新实现getDescription()方法,这个在具体的Condiment(调料)类中讲解
public abstract String getDescription();
}
具体饮料
- 浓缩咖啡Espresso
public class Espresso extends Beverage { public Espresso() { //该变量继承自Beverage description = "Espresso"; } public double cost() { //现在不管调料的价格,直接返回Espresso的价格$1.99 return 1.99; } }
- 综合咖啡HouseBlend
public class HouseBlend extends Beverage{ public Espresso() { //该变量继承自Beverage description = "HouseBlend"; } public double cost() { //现在不管调料的价格,直接返回Espresso的价格$1.99 return 0.89; } }
具体配料 Mocha
public class Mocha extends CondimentDecorator {
Beverage beverage;
//把饮料当做构造器参数,可以让被装饰者(饮料)被记录到实例变量中
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
//这里解释上面抽象配料描述设置为抽象的原因
//我们希望打印的不仅是饮料,而是可以完整的将配料同时写出来
return beverage.getDescription() + ", Mocha";
}
public double cost() {
//计算带Mocha饮料的价格,调用委托被装饰的对象计算价格然后加上Mocha价格,得到最后的结果
return 0.20+beverage.cost();
}
}
测试类 starBuzzCoffee
public class starBuzzCoffee{
public static void main(String[] args) {
//定制一杯不要饮料的Espresso
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()+"$"+beverage.cost());
//定制一份加了双摩卡黑暗考的饮料
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()+"$"+beverage2.cost());
}
}
应用情况
- 需要扩展一个类,或给一个类附加责任
- 需要给一个对象动态增加功能,同时可以撤销
- 需要增加一些基本功能的排列组合而产生大量的功能,从而继承关系变得不现实