1 设计原则
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
- 为交互对象之间的松耦合设计而努力
- 对扩展开发,对修改关闭
2 装饰者模式
- 动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
- 装饰者和被装饰对象有相同的超类型
- 可以用一个或多个装饰者包装一个对象
- 用装饰过的对象替代原来的对象
- 装饰者可以在所委托被装饰者的行为之前/后,加上自己的行为
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用喜欢的装饰者来装饰对象
- 装饰者模式比继承更具有弹性。
3 举例
点餐台应用场景:
- 咖啡做法有4种:DarkRoast(深度烘焙 )、Decat(德卡)、Espresso(浓咖啡)和HouseBlend(混合)四种,且以后可能会增加种类。
- 咖啡调料有2种:Mocha(加摩卡)、Whip(加奶泡),且以后可能会增加种类。
现在,客户点了如下的咖啡:深度烘焙且加摩卡和奶泡的咖啡。
代码要求:易扩展、松耦合
1、第一步:拿一个深焙(DarkRoast)对象;
2、第二步:以摩卡(Mocha)对象装饰它;
3、第三步:以奶泡(Whip)对象装饰它;
4、第四步:调用cost()方法,并依赖委托(delegate)将调料的价格加上去。
设计原则:对扩展开发,对修改关闭
类图:
4 Java实现
4.1 创建Beverage
基类和CondimentDecorator
装饰器
第一步:创建Beverage
抽象类
//Beverage是一个抽象类,有两个方法:getDescription() 和 cost()
public abstract class Beverage {
protected String description = "Unknown Beverage";
//getDescription() 已经在这里实现了,但是cost()必须在子类中实现
public String getDescription() {
return description;
}
public abstract double cost();
}
第二步:创建CondimentDecorator
装饰器,继承Beverage
//首先,必须让CondimentDecorator能够取代 Beverage,所以将 CondimentDecorator扩展 Beverage类
public abstract class CondimentDecorator extends Beverage{
//所有的调料装饰者都必须重新实现getDescription()方法。稍后我们会解释为什么......
public abstract String getDescription();
}
4.2 创建“做法”类
1.做法一:DarkRoast
public class DarkRoast extends Beverage{
public DarkRoast() {
description = "Dark Roast Coffee";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 1.13;
}
}
2.做法二:Decat
public class Decat extends Beverage{
public Decat() {
description = "Decat Coffee";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 1.67;
}
}
3.做法三:Espresso
//浓缩咖啡
//首先,让Espresso扩展自Beverage类,因为Beverage是一种饮料
public class Espresso extends Beverage {
public Espresso() {
//为了要设置饮料的描述,写了一个构造器。description 实例变量继承来自Beverage
description = "Espresso";
}
@Override
public double cost() {
// TODO Auto-generated method stub
//计算Espresso的价钱
return 1.99;
}
}
4.做法四:HouseBlend
public class HouseBlend extends Beverage{
public HouseBlend() {
description = "House Blend Coffee";
}
@Override
public double cost() {
// TODO Auto-generated method stub
//HouseBlend的价钱
return 0.89;
}
}
4.3 创建调料
1.调料一:Mocha
//摩卡是一个装饰者,所以让它扩展自CondimentDecorator
//CondimentDecorator扩展至Beverage
public class Mocha extends CondimentDecorator{
/*
* 要让Mocha能够引用一个Beverage,做法如下:
* 1.用一个实例变量记录饮料,也就是被装饰者。
* 2.想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。
*/
Beverage beverage;
//将上一个beverage传进来,用新的取代旧的
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
// 这里希望叙述不只是描述饮料(如“DarkRoast”),而是完整地连饮料都描述出来(如“DarkRoast, Mocha”)。
// 所以首先利用被委托的做法,得到一个叙述,然后再附加上本实例的叙述。
return beverage.getDescription()+", Mocha";
}
@Override
public double cost() {
// TODO Auto-generated method stub
// 首先调用委托给被装饰对象,已计算价钱。然后再加上Mocha的价钱
return 0.20 + beverage.cost();
}
}
2.调料二:Whip
// Whip是一个装饰者,所以让它扩展自CondimentDecorator
// CondimentDecorator扩展至Beverage
public class Whip extends CondimentDecorator{
/*
* 要让Whip能够引用一个Beverage,做法如下:
* 1.用一个实例变量记录饮料,也就是被装饰者。
* 2.想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。
*/
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
// 这里希望叙述不只是描述饮料(如“DarkRoast”),而是完整地连饮料都描述出来(如“DarkRoast, Whip”)。
// 所以首先利用被委托的做法,得到一个叙述,然后再附加上本实例的叙述。
return beverage.getDescription()+", Whip";
}
@Override
public double cost() {
// TODO Auto-generated method stub
// 首先调用委托给被装饰对象,已计算价钱。然后再加上Whip的价钱
return 0.66 + beverage.cost();
}
}
当然还可以创建很多哦。。。
4.4 举例测试
/*
* 抽象组件:Beverage
* 具体组件:HouseBlend
* 抽象装饰:CondimentDecorator
* */
public class StarbuzzCoffee {
public static void main(String args[]) {
//定一杯Espresso,不需要调料,现在打印出名字和价格
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()+" $" + beverage.cost());
//定一杯DarkRoast,加Mocah,现在打印出名字和价格
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription()+" $" + beverage2.cost());
//定一杯HouseBlend, 加Mocah,现在打印出名字和价格
Beverage beverage3 = new HouseBlend();
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()+" $" + beverage3.cost());
}
}
输出:
Espresso $1.99
Dark Roast Coffee, Mocha $1.3299999999999998
House Blend Coffee, Mocha, Whip $1.75
要是不想自己敲,代码下载
提取码:1wra