1. 概念
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态地添加功能,而无需修改其源代码。这种模式通过将对象包装在一个装饰器类的实例中来实现。装饰器类具有与原始对象相同的接口,这样客户端就无需知道装饰器和原始对象之间的区别。
装饰器模式的角色和工作原理:
-
组件接口(Component Interface):
- 定义了被装饰对象和装饰器共同实现的接口或抽象类。
-
具体组件(Concrete Component):
- 实现了组件接口的具体对象,可以被装饰或包装。
-
装饰器(Decorator):
- 持有一个指向组件对象的引用,并实现了与组件接口相同的接口。
- 通常是一个抽象类,其子类可以添加额外的功能。
-
具体装饰器(Concrete Decorator):
- 继承自装饰器类,实现了额外的功能。
- 在执行额外功能的同时,通过调用父类的方法来保留原始对象的行为。
装饰器模式的优点:
- 遵循开闭原则:可以在不修改现有代码的情况下增加新功能。
- 灵活性:可以通过组合不同的装饰器来实现不同的行为组合,而不是静态继承关系。
- 单一职责原则:每个装饰器类实现单一功能,易于管理和维护。
装饰器模式的应用场景:
- 动态增加功能:当需要在不改变现有对象结构的情况下,动态地添加功能或责任时,装饰器模式很有用。
- 多层装饰:可以通过叠加多个装饰器来实现复杂的行为组合,比如 I/O 流的处理。
- 维护已有代码:当需要在不破坏已有代码结构的前提下,修改或扩展功能时,装饰器模式可以被应用。
2. 示例
下面是一个使用Java实现装饰器模式的示例,我们将以咖啡和装饰器为例。首先定义一个咖啡接口 Coffee
和具体的咖啡类 SimpleCoffee
,然后创建装饰器抽象类 CoffeeDecorator
和具体的装饰器类 MilkDecorator
和 SugarDecorator
。
// 定义咖啡接口
interface Coffee {
double getCost(); // 获取咖啡价格
String getDescription(); // 获取咖啡描述
}
// 具体咖啡类
class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 1.0; // 基础咖啡价格
}
@Override
public String getDescription() {
return "Simple Coffee"; // 基础咖啡描述
}
}
// 装饰器抽象类
abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double getCost() {
return decoratedCoffee.getCost();
}
@Override
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// 具体装饰器类:牛奶装饰器
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 0.5; // 加牛奶的价格
}
@Override
public String getDescription() {
return super.getDescription() + ", Milk"; // 加牛奶的描述
}
}
// 具体装饰器类:糖装饰器
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 0.2; // 加糖的价格
}
@Override
public String getDescription() {
return super.getDescription() + ", Sugar"; // 加糖的描述
}
}
// 使用示例
public class DecoratorPatternExample {
public static void main(String[] args) {
// 创建一个简单咖啡
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Cost: " + simpleCoffee.getCost() + "; Description: " + simpleCoffee.getDescription());
// 使用装饰器为咖啡加牛奶
Coffee milkCoffee = new MilkDecorator(simpleCoffee);
System.out.println("Cost: " + milkCoffee.getCost() + "; Description: " + milkCoffee.getDescription());
// 使用装饰器为咖啡加牛奶和糖
Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
System.out.println("Cost: " + milkSugarCoffee.getCost() + "; Description: " + milkSugarCoffee.getDescription());
}
}
在上面的示例中,我们首先定义了 Coffee
接口,其中包含 getCost()
和 getDescription()
方法。然后创建了一个具体的咖啡类 SimpleCoffee
,实现了 Coffee
接口。接着,定义了装饰器抽象类 CoffeeDecorator
,它也实现了 Coffee
接口,并包含一个对 Coffee
对象的引用。具体的装饰器类 MilkDecorator
和 SugarDecorator
继承自 CoffeeDecorator
,并分别扩展了咖啡的价格和描述。
在 DecoratorPatternExample
类的 main
方法中,我们创建了一个简单的咖啡对象 SimpleCoffee
,然后使用装饰器 MilkDecorator
和 SugarDecorator
来动态地添加牛奶和糖。每个装饰器都会修改咖啡的价格和描述,但不改变原始咖啡对象的类结构。
这个示例展示了装饰器模式的灵活性和可扩展性,我们可以随时组合不同的装饰器来增强对象的功能,而不需要修改原始对象的代码。