1、概念
装饰模式动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活,属于结构性模式一种。
2、模式结构
- 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
- 具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
- 抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
- 具体装饰器角色(ConcreteDecorator):向组件添加职责。
3、使用场景
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类)
4、优缺点
优点:
- 装饰模式可以提供比继承更多的灵活性
- 通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为
- 使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合
- 具体构件类与具体装饰类可以独立变化,原有代码无须改变,符合“开闭原则”
缺点:
- 装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度
- 多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐
5、实例
在购买奶茶的时候,经常会有选择配料的选项,每种配料的价格不一样,可以多种组合,价格也不一样。首先先定义奶茶的接口,具有名称和价格方法
public interface IMilkTea {
String name();
double price();
}
继承IMilkTea的相关茶类
public class RedTea implements IMilkTea {
@Override
public String name() {
return "红茶";
}
@Override
public double price() {
return 10;
}
}
public class GreenTea implements IMilkTea {
@Override
public String name() {
return "绿茶";
}
@Override
public double price() {
return 12;
}
}
定义具体装饰类Decorator,装饰相关奶茶
public class Decorator implements IMilkTea {
@Override
public String name() {
return null;
}
@Override
public double price() {
return 0;
}
}
继承Decorator的相关类
public class IceCream extends Decorator {
private String name = "加雪糕";
private IMilkTea milkTea;
public IceCream(IMilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String name() {
return milkTea.name() + name;
}
@Override
public double price() {
return milkTea.price() + 3;
}
}
public class Pearl extends Decorator {
private String name = "加珍珠";
private IMilkTea milkTea;
public Pearl(IMilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String name() {
return milkTea.name() + name;
}
@Override
public double price() {
return milkTea.price() + 2;
}
}
客户端使用
public static void main(String[] args) {
IMilkTea milkTea = new RedTea();
milkTea = new IceCream(milkTea);
milkTea = new Pearl(milkTea);
System.out.println(milkTea.name() + "
价格:" + milkTea.price());
}