概述
装饰者模式(Decorator Pattern),别名(Warpper)。 动态的将责任附加到对象上。若要扩展功能,装饰者提供比继承更有弹性的替代方案。
装饰者模式从属的大类是结构型模式(该大类下的设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式)。
涉及的设计原则:
1. 开放–关闭原则类(应该对扩展开放,对修改关闭)
2. 多用组合,少用继承
类图
需要注意的东西
由上面类图可知,装饰者模式,由Component(抽象组件)、ConcreteComponent(具体组件)、Decorator(抽象装饰类)、ConcreteDecorator(具体装饰类)组成。其主要功能从上面类图可以看出。在此需要注意几点:
- 上述中Components和Decorator均可以是接口也可以是抽象类。
- 装饰者和被装饰者需要实现相同的接口或者抽象类。即装饰者和被装饰者对象拥有相同的超类。
- 可以用一个或多个装饰者包装一个对象。
- 装饰者模式的用意是包吃接口并增加对象的职责。
- 一张图看懂装饰者模式流程。
应用场景
星巴克咖啡对其出售的咖啡的一种销售策略的价格做一下统计、这样当顾客点了他们想要的搭配的时候我们就可以知道最后的价格是多少。
假设有四种咖啡:HouseBlend、DarkRoaat、Decaf、Espresso。
同样有四种配料可以选择:Milk、Mocha、Soy、Mocha。
这四种咖啡有不同的价格、同样四种搭配也有不同的价格。当我们把这个单子拿给顾客的时候、顾客可以任意点一种咖啡、可以任意搭配。我们的目的是要统计每种搭配的价格、那我们要怎么来实现呢?当我们要添加一个或多个新的品种或者配料的时候,又如何有效的解决这个问题呢?这就是装饰者模式的解决的问题。
代码实现
这里为了简化代码,只采用两种咖啡HouseBlend、Espresso,两种配料Mocha、Mocha,这样的搭配。
package decorator;
/**
* <p>ClassName Beverage
* <p>Description 超类
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 0:51
*/
public abstract class Beverage {
String description = "unknow Beverage";
public String getDescription() {
return description;
}
/**
* 计算花费
*
* @return
*/
public abstract Double cost();
}
package decorator;
/**
* <p>ClassName CondimentDecorator
* <p>Description 配料抽象类
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 0:55
*/
public abstract class CondimentDecorator extends Beverage {
/**
* 所有的配料装饰者都需要重写getDescription
*/
public abstract String getDescription();
}
package decorator;
/**
* <p>ClassName Espresso
* <p>Description 一种饮品(具体的组件)
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 1:01
*/
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}
public Double cost() {
return 1.99;
}
}
package decorator;
/**
* <p>ClassName HouseBlend
* <p>Description 另外一种饮品(具体的组件)
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 1:04
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "HouseBlend";
}
public Double cost() {
return 0.89;
}
}
package decorator;
/**
* <p>ClassName Mocha
* <p>Description 具体装饰者
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 1:05
*/
public class Mocha extends CondimentDecorator {
Beverage beverage; //被装饰者
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ",Mocha";
}
public Double cost() {
return 0.2 + beverage.cost();
}
}
package decorator;
/**
* <p>ClassName Whip
* <p>Description 具体装饰者
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 1:12
*/
public class Whip extends CondimentDecorator {
Beverage beverage; //被装饰者
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ",Whip";
}
public Double cost() {
return 66.66 + beverage.cost();
}
}
package decorator;
/**
* <p>ClassName TestDecorator
* <p>Description 测试类
* <p>Author ChongLou
* <p>Version
* <p>Date 2017/7/24 1:09
*/
public class TestDecorator {
public static void main(String[] args) {
/**
* 一杯Espresso,不加配料
*/
Beverage beverage = new Espresso();
System.out.println( beverage.getDescription() + " $" + beverage.cost() );
/**
* 一杯Houseblend,加Mocha,Whip
*/
Beverage beverage1 = new HouseBlend();
beverage1 = new Mocha( beverage1 );
beverage1 = new Whip( beverage1 );
System.out.println( beverage1.getDescription() + " $" + beverage1.cost() );
}
}
运行结果
总结
又get一种新的pattern。
装饰者模式比继承更加灵活机动,但是不要忘记了装饰者模式的别名warpper,如果过度使用,多层warpper,会使程序变得很复杂。