定义
装饰者模式动态地将额外责任附加到对象上。对于扩展功能,装饰者提供子类化之外的弹性替代方案。
- 装饰模式是在不必改变原类和使用继承的情况下,动态地扩展一个对象的功能。
- 它通过创建一个包装对象,也就是装饰来包裹真实的对象。
构成
- 抽象构件(Component):
- 定义了一个接口或抽象类,规范了具体组件和装饰类需要实现的方法。
- 具体构件(ConcreteComponent):
- 实现了抽象构件接口的具体类,这是我们要动态扩展功能的原有对象。
- 抽象装饰(Decorator):
- 装饰者共同要实现的接口,通常是抽象类。
- 持有一个抽象构件的引用,用来接收被装饰的对象。
- 具体装饰(ConcreteDecorator):
- 继承自抽象装饰类,是具体的装饰者类。
- 实现特定的功能扩展,通过组合方式将新功能附加到被装饰的对象上。
优点
- 灵活性:装饰者模式允许在运行时动态地给对象添加或者撤销功能功能,而无需修改其原始类。这使得系统更加灵活,可以根据需要组合不同的装饰者。
- 避免类爆炸:相比于继承,装饰者模式避免了类爆炸问题。通过组合装饰者,我们可以实现多样化的功能扩展,而不需要创建大量的子类。
- 单一职责原则:装饰者模式遵循单一职责原则,每个装饰者只关注一个特定的功能。
缺点
- 复杂性增加:引入装饰者会增加类的数量,可能导致代码变得复杂。维护多个装饰者之间的关系也需要一定的注意。
- 运行时性能开销:由于装饰者的嵌套,可能会在运行时产生一些性能开销。尽管通常不会显著影响性能,但在某些情况下需要考虑。
场景
1. 在文本编辑器中,动态地添加颜色、字体等样式的功能,而不需要改变原有的文本对象
2. 在游戏中,可以使用装饰者模式来扩展角色的能力,而不需要创建大量的子类
3. 在订单处理系统中,可以动态地添加或取消订单的附加服务
4. 在电商网站中,可以动态地添加促销活动、折扣等功能
案例
描述
一个奶茶店需要一个能够快速计算出一杯饮料价钱的功能,在基础饮料(茶,咖啡,果汁等)中会添加其他调料(糖,牛奶等)
思路
可以把设计一个抽象组件类使所有的饮料,继承在这只是为了统一类型,不统一行为。调料是装饰者,所以也需要抽象出一个描述所有抽象者的类为了统一类型这个类也要继承组件类。在进行对不同饮料的加料使只需要对应的调料和饮料进行组合
类图
代码
Beverage(饮料)
//抽象组件类
public abstract class Beverage {
String description = "未知的饮料";
public String getDescription(){
return description;
}
public abstract double cost();
}
CondimentDecorator(调料)
//装饰组件类
public abstract class CondimentDecorator extends Beverage{
Beverage beverage;
public abstract String getDescription();
}
Espresso(咖啡)
public class Espresso extends Beverage{
public Espresso(){
description = "浓缩咖啡";
}
@Override
public double cost() {
return 15;
}
}
GreenTea(绿茶)
public class GreenTea extends Beverage{
public GreenTea(){
description = "绿茶";
}
@Override
public double cost() {
return 15;
}
}
Milk(用作调料的牛奶)
public class Milk extends CondimentDecorator{
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost()+5;
}
@Override
public String getDescription() {
return beverage.getDescription()+"加牛奶";
}
}
Sugar(糖)
public class Sugar extends CondimentDecorator{
public Sugar(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost()+2;
}
@Override
public String getDescription() {
return beverage.getDescription()+"加糖";
}
}
测试类
public class Test {
public static void main(String[] args) {
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription()+"-->"+espresso.cost()+"元");
Beverage teaAddMilk = new Milk(new GreenTea());
System.out.println(teaAddMilk.getDescription()+"-->"+teaAddMilk.cost()+"元");
Beverage espressoAddMilk = new Milk(new Espresso());
System.out.println(espressoAddMilk.getDescription()+"-->"+espressoAddMilk.cost()+"元");
Beverage espressoAddMilkAddSugar = new Sugar(new Milk(new Espresso()));
System.out.println(espressoAddMilkAddSugar.getDescription()+"-->"+espressoAddMilkAddSugar.cost()+"元");
}
}
//运行结果
浓缩咖啡-->15.0元
绿茶加牛奶-->20.0元
浓缩咖啡加牛奶-->20.0元
浓缩咖啡加牛奶加糖-->22.0元
理解
对观察者模式进行使用的时候只需要将被装饰对象传递给装饰者,可以一直传递到该对象已经被装饰的满足要求为止,在不对原代码修改的情况下添加了新的功能,这能使我们很灵活的去对原有对象扩展新的功能,这也是装饰者模式所解决的问题。