Java 设计模式-装饰器模式
Java 设计模式-装饰器模式
模式概述
装饰器模式简介
定义:它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
模式类型:结构型
作用:
- 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
- 可以在不修改原有代码的情况下,通过组合的方式扩展对象的功能,符合开闭原则(对扩展开放,对修改关闭)。
- 提供了比继承更有弹性的替代方案,避免了因为子类继承导致的类爆炸问题。
典型应用场景:
- 在图形界面开发中,为组件添加滚动条、边框等装饰
- Java 的 I/O 流体系中就大量使用了装饰器模式
我认为:装饰器将对象包装,在不改变类的情况下增加其功能,是一种相比继承更灵活的替代方案。
课程目标
-
理解装饰器模式的核心思想和经典应用场景
-
识别应用场景,使用装饰器模式解决灵活扩展对象的功能要求
-
了解装饰器模式的优缺点
核心组件
角色-职责表
角色 | 职责 | 示例类名 |
---|---|---|
抽象组件(Component) | 定义一个对象接口,可以给这些对象动态地添加职责。 | Coffee |
具体组件(ConcreteComponent) | 实现了抽象组件的接口,是待装饰的对象。 | SimpleCoffee |
抽象装饰器(Decorator) | 继承自抽象组件,并持有一个抽象组件的引用。 | CoffeeDecorator |
具体装饰器(ConcreteDecorator) | 实现了抽象装饰器,负责给具体组件添加具体的职责。 | MilkDecorator, SugarDecorator |
3.2 类图
传统实现 VS 装饰者模式
传统实现(痛点版)
代码实现:
// 传统方式:通过继承扩展功能
public class SimpleCoffee {public double getCost() {return 2.0;}public String getDescription() {return "Simple Coffee";}
}// 加牛奶的咖啡
public class MilkCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.5;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk";}
}// 加糖的咖啡
public class SugarCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Sugar";}
}// 加牛奶和糖的咖啡 → 需要创建 MilkAndSugarCoffee,甚至更多组合
public class MilkAndSugarCoffee extends SimpleCoffee {@Overridepublic double getCost() {return super.getCost() + 0.5 + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk, Sugar";}
}
痛点总结:
- ❌ 类爆炸:每种功能组合都需要一个新类,N 个功能会产生 2^N 个子类。
- ❌ 代码重复:每个子类都要重写
getCost()
和getDescription()
,逻辑重复。 - ❌ 不灵活:无法在运行时动态添加或移除功能。
- ❌ 违反开闭原则:新增功能需要修改父类或增加大量子类。
装饰器模式(优雅版)
代码实现:
// 1. 抽象组件
public interface Coffee {double getCost();String getDescription();
}// 2. 具体组件
public class SimpleCoffee implements Coffee {@Overridepublic double getCost() {return 2.0;}@Overridepublic String getDescription() {return "Simple Coffee";}
}// 3. 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {protected Coffee coffee;public CoffeeDecorator(Coffee coffee) {this.coffee = coffee;}@Overridepublic double getCost() {return coffee.getCost();}@Overridepublic String getDescription() {return coffee.getDescription();}
}// 4. 具体装饰器
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.5;}@Overridepublic String getDescription() {return super.getDescription() + ", Milk";}
}public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() {return super.getCost() + 0.3;}@Overridepublic String getDescription() {return super.getDescription() + ", Sugar";}
}// 使用示例
public class Main {public static void main(String[] args) {Coffee coffee = new SimpleCoffee();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());// 输出:Simple Coffee, Milk, Sugar $2.8}
}
优势:
- ✅ 运行时动态扩展:可以在程序运行时自由组合功能。
- ✅ 符合开闭原则:新增功能只需添加新的装饰器类,无需修改已有代码。
- ✅ 避免类爆炸:通过组合而非继承,有效减少子类数量。
- ✅ 职责清晰:每个装饰器只关注一个功能点,符合单一职责原则。
局限:
- ⚠️ 装饰顺序影响结果:如日志装饰器和加密装饰器顺序不同,行为可能不同。
- ⚠️ 过度使用导致复杂度上升:装饰链过长时,调试和理解成本增加。
- ⚠️ 仅适用于支持接口/抽象基类的场景:被装饰对象必须有统一接口。
每次调用都是一次递归操作。
模式变体
- 链式装饰器:通过责任链模式控制处理顺序
- 优先级装饰器:支持按优先级排序的装饰器
- 条件装饰器:根据业务规则动态决定是否应用装饰
- 组合装饰器:将多个装饰器打包成复合装饰器
最佳实践
建议 | 理由 |
---|---|
保持单一职责 | 每个装饰器只处理一个业务逻辑 |
使用工厂模式 | 管理复杂装饰器组合的创建过程 |
记录装饰日志 | 方便追踪处理流程和调试 |
限制装饰层级 | 避免过度复杂的装饰链 |
提供默认实现 | 确保基础功能的完整性 |
一句话总结
装饰器模式通过优雅的包装机制,实现了来一杯咖啡功能的动态组合与灵活扩展,完美解决了传统实现中的代码臃肿和扩展性难题。
如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊