介绍
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在不改变对象自身的情况下,动态地为对象添加额外的职责。这个模式通常用于增强或改变对象的功能。
1.定义
装饰模式通过创建一个装饰类,将功能动态地添加到被装饰类的实例中。装饰类与被装饰类实现相同的接口或继承相同的父类,这样装饰对象就可以取代被装饰对象。
2. 主要作用
- 动态地给一个对象添加额外的职责,而不改变其原本的结构。
- 提供了比继承更灵活的替代方案,通过组合而不是继承来扩展对象的功能。
3. 解决的问题
- 需要在运行时为对象动态添加功能,而不修改对象本身。
- 需要避免因类数量过多而导致的复杂性。
- 需要在不影响其他对象的情况下扩展功能。
4. 模式原理
包含角色:
- 组件接口(Component): 定义了一个接口,用于所有具体组件和装饰器。
- 具体组件(ConcreteComponent): 实现了组件接口的具体类。
- 装饰器(Decorator): 实现了组件接口并持有一个组件的引用,用于在其方法中调用被装饰对象的方法。
- 具体装饰器(ConcreteDecorator): 继承自装饰器类,添加额外的功能。
UML类图:
代码示例:
// Component接口
public interface Coffee {
double cost();
String description();
}
// ConcreteComponent具体组件
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 5;
}
@Override
public String description() {
return "Simple Coffee";
}
}
// Decorator装饰器
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
@Override
public double cost() {
return decoratedCoffee.cost();
}
@Override
public String description() {
return decoratedCoffee.description();
}
}
// ConcreteDecorator具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 1.5;
}
@Override
public String description() {
return super.description() + ", Milk";
}
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 0.5;
}
@Override
public String description() {
return super.description() + ", Sugar";
}
}
调用
public class DecoratorPatternExample {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println(simpleCoffee.description() + " $" + simpleCoffee.cost());
Coffee milkCoffee = new MilkDecorator(simpleCoffee);
System.out.println(milkCoffee.description() + " $" + milkCoffee.cost());
Coffee milkSugarCoffee = new SugarDecorator(milkCoffee);
System.out.println(milkSugarCoffee.description() + " $" + milkSugarCoffee.cost());
}
}
打印输出
Simple Coffee \$5.0
Simple Coffee, Milk \$6.5
Simple Coffee, Milk, Sugar \$7.0
上面示例,通过使用装饰者模式,程序能够灵活地添加新功能而不需要修改原有的 Coffee 实现。
装饰模式看似很简单,其实一点也不复杂😁,我相信看完一遍示例,再回头看下定义就会全懂了。
那么看似一个简单的设计模式,在我们开发中有哪些应用场景呢?下面举几个经典示例来加深下印象:
1.Java的输入/输出(I/O)库中使用了装饰者模式来增强流的功能。
例如,BufferedInputStream
和 DataInputStream
是装饰者,它们分别装饰 InputStream
类。通过这些装饰者,可以为基础流添加缓冲、数据格式等额外的功能。
emm… 上一篇文章,桥接模式也是举的这个例子,看来Java SDK使用的设计模式不少啊。
InputStream fileStream = new FileInputStream("file.txt");
BufferedInputStream bufferedStream = new BufferedInputStream(fileStream);
DataInputStream dataStream = new DataInputStream(bufferedStream);
2.Android View 组件
安卓开发的同学肯定都自定义过View
吧,例如,创建一个自定义的TextView
,通过扩展原始的TextView
并添加一些额外的样式或功能。
public class CustomTextView extends TextView {
public CustomTextView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 添加自定义绘制逻辑
}
}
还有在 使用 Retrofit
网络请求时,可以自定义的拦截器,Spring
框架的 AOP
通过代理(proxy)对象,可以在不改变原始对象的情况下,动态地添加横切关注点(如日志记录、事务管理等)等等… 这些都是装饰模式的经典案例。可能我们平时没注意,不知道它们用的是装饰模式,只要记住只要是 在运行时动态地添加行为和功能就可以认为是装饰者模式即可。
5. 优缺点
优点:
- 灵活性:可以动态地添加或修改对象的功能,而无需修改原始类。
- 扩展性:可以通过添加新的装饰器类来扩展功能,不需要改变现有代码。
- 符合开闭原则:原有类不需要改变,新增功能通过装饰器实现。
缺点:
- 复杂性:使用装饰模式会增加系统的复杂性,特别是在多个装饰器层叠使用时。
- 调试困难:由于装饰者的层级结构,调试时可能不容易追踪到具体的功能实现。
6. 应用场景
- 在图形界面中,可以为组件如按钮、文本框等添加滚动条、边框等装饰。
- 在Java的IO系统中,装饰模式被广泛应用于各种输入流和输出流的实现中,如BufferedInputStream装饰FileInputStream。
- 需要在不改变原有类的情况下,动态地为对象添加功能。
7. 总结
装饰模式是一种强大的设计模式,通过组合对象和装饰器的方式实现功能的动态扩展。它在实际开发中非常有用,能够有效地提高代码的灵活性和可维护性。虽然引入了额外的复杂性,但在许多情况下,这种复杂性是值得的,因为它使得系统可以更容易地适应变化和需求。