定义
装饰者模式:动态地将责任附加到对象身上。若要扩展功能,将要比继承更加灵活,更富有弹性。
设计原则
- 封装变化。
- 少用继承,多用组合。
- 针对接口编程,不针对实现编程,更易于维护和扩展,更有条理。
- 交互对象之间的松耦合设计,是程序更具有弹性。
- 对扩展开放,对修改关闭
结构图
组成对象
- 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
- 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
- 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
- 具体装饰器角色(ConcreteDecoratorA,B…):向组件添加职责。
重点
- 装饰者和被装饰者(真实对象)具有相同的超类
- 可以有一个或者多个装饰者包装一个对象
- 可以用装饰者对象替代真实对象,可以在运行是动态的装饰对象
- 装饰者可以在真实对象执行自己的行为前/后加上自己的行为,达到特定目的
典型案例
下面就根据head first设计模式书中的例子,来看看星巴兹店是如何卖咖啡的。在计算不同组合的订单如何计算最终价格,以及出现新的调料或者咖啡品种,如何在不修改原有的代码基础上扩展程序——对扩展开发,对修改关闭
来看一下最终设计的类结构图
案例中对应的角色
1. 抽象组件角色(Component):Beverage
2. 具体组件角色(ConcreteComponent):DarkRoast、Decaf、Espresso、HouseBlend
3. 抽象装饰器(Decorator):CondimentDecorator
4. 具体装饰器角色(ConcreteDecoratorA,B…):Mocha、Soy、Whip
参考代码
饮料抽象类——Beverage
/**
* 饮料抽象类
* @author: Yang Gao
* @date: 2017-4-6 下午2:45:31
* @version: 1.0
*/
public abstract class Beverage {
protected String description = "Unknow Beverage!";
public String getDescription() {
return description;
}
// 定义饮料的单价
public abstract double cost();
}
具体实现类,真实对象,被装饰者,各种饮料类
/**
* 饮料:深度烘焙
* @author: Yang Gao
* @date: 2017-4-6 下午3:46:20
* @version: 1.0
*/
public class DarkRoast extends Beverage {
public DarkRoast() {
this.description = "DarkRoast";
}
public double cost() {
return .99;
}
}
/**
* 饮料:低咖啡因
* @author: Yang Gao
* @date: 2017-4-6 下午2:51:22
* @version: 1.0
*/
public class Decaf extends Beverage {
public Decaf() {
this.description = "Decaf";
}
@Override
public double cost() {
return 1.99d;
}
}
/**
* 饮料:浓咖啡
* @author: Yang Gao
* @date: 2017-4-6 下午2:51:22
* @version: 1.0
*/
public class Espresso extends Beverage {
public Espresso() {
this.description = "Espresso";
}
@Override
public double cost() {
return 1.99d;
}
}
/**
* 饮料:混合
* @author: Yang Gao
* @date: 2017-4-6 下午3:47:58
* @version: 1.0
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
this.description = "House Blend Coffee";
}
public double cost() {
return .89;
}
}
装饰抽象类, 调料抽象类
/**
* 调料抽象类:装饰饮料类
* @author: Yang Gao
* @date: 2017-4-6 下午2:45:31
* @version: 1.0
*/
public abstract class CondimentDecorator extends Beverage{
protected Beverage beverage;
public CondimentDecorator(Beverage beverage){
this.beverage = beverage;
}
public abstract String getDescription();
}
具体装饰者
/**
* 调料:摩卡
* @author: Yang Gao
* @date: 2017-4-6 下午2:53:53
* @version: 1.0
*/
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return super.beverage.getDescription() + ", Mocha";
}
@Override
public double cost() {
return .2d + super.beverage.cost();
}
}
/**
* 调料:豆浆
* @author: Yang Gao
* @date: 2017-4-6 下午2:53:53
* @version: 1.0
*/
public class Soy extends CondimentDecorator {
public Soy(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return super.beverage.getDescription() + ", Soy";
}
@Override
public double cost() {
return .15d + super.beverage.cost();
}
}
/**
* 调料:奶泡
* @author: Yang Gao
* @date: 2017-4-6 下午2:53:53
* @version: 1.0
*/
public class Whip extends CondimentDecorator {
public Whip(Beverage berevage) {
super(berevage);
}
@Override
public String getDescription() {
return super.beverage.getDescription() + ", Whip";
}
@Override
public double cost() {
return .1d + super.beverage.cost();
}
}
客户端测试类
/**
* 咖啡店
* @author: Yang Gao
* @date: 2017-4-6 下午2:57:20
* @version: 1.0
*/
public class StarbuzzCoffee {
public static void main(String[] args) {
// 1.来一杯浓咖啡
Espresso beverage = new Espresso();
System.out.println("一杯浓咖啡:" + beverage.getDescription() + ", $" + beverage.cost());
// 2.来一杯双倍摩卡奶泡深度烘焙
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println("一杯双倍摩卡奶泡深度烘焙:" + beverage2.getDescription() + ", $" + beverage2.cost());
// 3.来一杯摩卡奶泡豆浆混合
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println("一杯摩卡奶泡豆浆浓混合:" + beverage3.getDescription() + ", $" + beverage3.cost());
}
运行结果