装饰器模式 (Decorator Pattern)
装饰器模式是一种 结构型设计模式,它允许动态地向对象添加新的功能,同时又不改变其结构。这种模式通过对象组合来实现,比通过继承扩展功能更加灵活。
原理
- 核心思想:通过将对象包装在一系列装饰器中,增强或修改对象的功能,而无需更改对象本身的代码。
- 适用场景:
- 希望动态地扩展一个类的功能,而不使用继承。
- 需要为一个对象提供多种行为组合,且行为的数量动态变化。
- 参与角色:
- Component(抽象组件):定义一个对象接口,可以动态地为其添加职责。
- ConcreteComponent(具体组件):实现抽象组件的类,是被装饰的对象。
- Decorator(装饰器):实现抽象组件,并持有一个具体组件的引用。
- ConcreteDecorator(具体装饰器):扩展装饰器,提供额外的功能。
优点
- 动态扩展功能:无需修改原始类和其他装饰器类即可扩展功能。
- 遵循开闭原则:通过添加新装饰器类来实现功能扩展。
- 灵活组合:装饰器可以任意组合使用。
缺点
- 复杂性增加:过多的装饰器可能导致系统变得复杂。
- 调试困难:在装饰器链中定位问题可能较为困难。
示例代码
场景描述
假设有一个咖啡订单系统,不同的咖啡可以加不同的调料(如牛奶、糖)。咖啡本身和调料的价格需要动态组合计算,且系统应具备良好的扩展性。
1. 定义抽象组件
// 抽象组件
public interface Coffee {
String getDescription(); // 获取描述
double getCost(); // 获取价格
}
2. 创建具体组件
// 具体组件:基础咖啡
public class BasicCoffee implements Coffee {
@Override
public String getDescription() {
return "Basic Coffee";
}
@Override
public double getCost() {
return 5.0;
}
}
3. 创建装饰器抽象类
// 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee; // 持有组件的引用
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
4. 创建具体装饰器
// 牛奶装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
@Override
public double getCost() {
return coffee.getCost() + 1.5; // 牛奶价格
}
}
// 糖装饰器
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", Sugar";
}
@Override
public double getCost() {
return coffee.getCost() + 0.5; // 糖价格
}
}
5. 客户端代码
public class DecoratorPatternExample {
public static void main(String[] args) {
// 基础咖啡
Coffee coffee = new BasicCoffee();
System.out.println(coffee.getDescription() + " -> $" + coffee.getCost());
// 加牛奶的咖啡
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + " -> $" + coffee.getCost());
// 加牛奶和糖的咖啡
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription() + " -> $" + coffee.getCost());
}
}
输出结果
Basic Coffee -> $5.0
Basic Coffee, Milk -> $6.5
Basic Coffee, Milk, Sugar -> $7.0
UML 类图
+----------------+ +--------------------+
| Coffee |<>------>| CoffeeDecorator |
+----------------+ +--------------------+
|+ getDescription() |+ getDescription() |
|+ getCost() |+ getCost() |
+----------------+ +--------------------+
^ ^
| |
+--------------------+ +--------------------+
| BasicCoffee | | MilkDecorator |
+--------------------+ +--------------------+
|+ getDescription() | |+ getDescription() |
|+ getCost() | |+ getCost() |
+--------------------+ +--------------------+
使用场景
- 需要动态地增加功能:如图形编辑器中为图形对象添加功能(边框、阴影)。
- 功能组合的场景:如不同装饰效果的叠加。
- 避免继承扩展:用装饰器动态扩展功能而不引入过多的子类。
总结
- 装饰器模式是一种灵活的设计模式,可以在运行时动态地为对象添加功能。
- 它有效避免了类爆炸问题,特别适合需要灵活组合的场景。
- 通过对象组合,装饰器模式实现了强大的扩展能力,符合 开闭原则。