装饰器模式
装饰器模式中文讲解
装饰器模式(Decorator Pattern)是一种结构型设计模式,属于 GoF(Gang of Four)提出的23种设计模式之一。它的核心目标是动态地为对象添加额外的职责或功能,而无需修改其代码。装饰器模式通过将对象包装在装饰器类中,扩展功能,是一种灵活的替代继承的方式,符合开闭原则(对扩展开放,对修改关闭)。
以下用中文详细讲解装饰器模式的定义、结构、代码示例、应用场景、优缺点以及在 Servlet 环境中的使用,重点突出其原理和实际应用。
1. 什么是装饰器模式?
装饰器模式允许在运行时为对象动态添加功能,而不改变其原有结构或行为。它通过创建一个包装类(装饰器)来扩展目标对象的功能,解决了以下问题:
- 问题:如果通过继承扩展对象功能,会导致子类数量激增(如为咖啡添加多种配料:牛奶、糖浆、奶泡),且不灵活。
- 解决方案:定义一个装饰器类,实现与目标对象相同的接口,并持有目标对象的引用,在调用目标方法时添加额外功能。
关键特点:
- 动态扩展:运行时添加功能,无需修改源代码。
- 组合优于继承:通过组合实现功能扩展,避免类爆炸。
- 透明性:客户端无需知道对象被装饰,接口保持一致。
2. 装饰器模式的结构
装饰器模式包含以下角色:
角色 | 描述 |
---|---|
抽象组件(Component) | 定义对象的公共接口,声明核心方法(如 operation() )。 |
具体组件(Concrete Component) | 实现抽象组件,提供基本功能。 |
装饰器(Decorator) | 实现抽象组件接口,持有组件引用,扩展功能。 |
具体装饰器(Concrete Decorator) | 为具体组件添加特定功能。 |
客户端(Client) | 通过组件接口操作对象,无需区分是否被装饰。 |
UML 类图(文字描述):
- Component:接口,定义
operation()
方法。 - ConcreteComponent:实现 Component,提供基本功能。
- Decorator:抽象类或接口,实现 Component,持有 Component 引用,调用其
operation()
。 - ConcreteDecorator:扩展 Decorator,添加额外功能。
- 客户端通过 Component 接口操作,可能层层包装。
3. 代码示例
以下是一个 Java 示例,模拟为咖啡添加配料(如牛奶、糖浆)。
// 抽象组件:饮料
interface Beverage {
String getDescription();
double cost();
}
// 具体组件:基础咖啡
class Coffee implements Beverage {
@Override
public String getDescription() {
return "基础咖啡";
}
@Override
public double cost() {
return 5.0;
}
}
// 装饰器:抽象装饰类
abstract class BeverageDecorator implements Beverage {
protected Beverage beverage; // 持有被装饰对象
public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription();
}
@Override
public double cost() {
return beverage.cost();
}
}
// 具体装饰器:牛奶
class MilkDecorator extends BeverageDecorator {
public MilkDecorator(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 牛奶";
}
@Override
public double cost() {
return beverage.cost() + 1.5;
}
}
// 具体装饰器:糖浆
class SyrupDecorator extends BeverageDecorator {
public SyrupDecorator(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + ", 糖浆";
}
@Override
public double cost() {
return beverage.cost() + 0.5;
}
}
// 测试
public class DecoratorTest {
public static void main(String[] args) {
// 基础咖啡
Beverage coffee = new Coffee();
System.out.println(coffee.getDescription() + " 成本: $" + coffee.cost());
// 添加牛奶
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getDescription() + " 成本: $" + coffee.cost());
// 再添加糖浆
coffee = new SyrupDecorator(coffee);
System.out.println(coffee.getDescription() + " 成本: $" + coffee.cost());
}
}
输出:
基础咖啡 成本: $5.0
基础咖啡, 牛奶 成本: $6.5
基础咖啡, 牛奶, 糖浆 成本: $7.0
说明:
Beverage
是抽象组件,定义getDescription()
和cost()
。Coffee
是具体组件,提供基础功能。BeverageDecorator
是抽象装饰器,持有Beverage
引用。MilkDecorator
和SyrupDecorator
添加额外功能,层层包装。- 客户端通过
Beverage
接口操作,无需知道装饰层。
4. 装饰器模式的应用场景
- 动态扩展功能:如为文件流添加加密、压缩功能(Java 的
InputStream
和OutputStream
)。 - UI 组件:如 Swing 组件添加边框、滚动条。
- 日志增强:为日志记录器添加时间戳、格式化。
- Servlet 相关:包装 HTTP 请求或响应,添加额外处理(如日志、权限检查)。
Servlet 示例:装饰 HTTP 响应,添加日志记录。
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Logger;
// 抽象组件:响应处理器
interface ResponseHandler {
void handle(HttpServletResponse resp) throws IOException;
}
// 具体组件:基础响应
class BasicResponseHandler implements ResponseHandler {
@Override
public void handle(HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<h1>基础响应</h1>");
}
}
// 装饰器:抽象装饰类
abstract class ResponseDecorator implements ResponseHandler {
protected ResponseHandler handler;
public ResponseDecorator(ResponseHandler handler) {
this.handler = handler;
}
@Override
public void handle(HttpServletResponse resp) throws IOException {
handler.handle(resp);
}
}
// 具体装饰器:添加日志
class LoggingDecorator extends ResponseDecorator {
private static final Logger logger = Logger.getLogger(LoggingDecorator.class.getName());
public LoggingDecorator(ResponseHandler handler) {
super(handler);
}
@Override
public void handle(HttpServletResponse resp) throws IOException {
logger.info("处理响应: 开始");
handler.handle(resp);
logger.info("处理响应: 完成");
}
}
// Servlet
public class DecoratorServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
ResponseHandler handler = new BasicResponseHandler();
// 添加日志装饰
handler = new LoggingDecorator(handler);
handler.handle(resp);
}
}
输出(浏览器):
<h1>基础响应</h1>
日志:
INFO: 处理响应: 开始
INFO: 处理响应: 完成
说明:
ResponseHandler
是抽象组件,定义响应处理接口。BasicResponseHandler
是基础响应。LoggingDecorator
装饰响应,添加日志记录。- Servlet 使用装饰器动态增强响应功能。
5. 装饰器模式的优缺点
优点:
- 动态扩展:运行时添加功能,无需修改原类。
- 灵活性:支持多层装饰,组合不同功能。
- 符合开闭原则:通过装饰器扩展功能,原类保持不变。
- 替代继承:避免继承导致的类爆炸。
缺点:
- 复杂性:多层装饰可能导致代码复杂,难以调试。
- 类数量增加:每个新功能需要一个装饰器类。
- 性能开销:多层包装可能引入少量调用开销。
6. 注意事项
- 接口一致性:
- 装饰器和组件必须实现相同接口,保持透明性。
- 避免装饰器添加过多额外方法,破坏接口一致性。
- 中文编码问题:
- 如果涉及中文(如响应内容),确保 Servlet 使用 UTF-8:
java resp.setContentType("text/html;charset=UTF-8");
- 对中文数据编码(如
URLEncoder.encode("响应", "UTF-8")
)避免乱码。
- 装饰器顺序:
- 装饰器调用顺序影响功能(如先日志后加密 vs 先加密后日志)。
- 客户端需明确装饰顺序。
- 性能优化:
- 避免过多装饰层,减少调用栈深度。
- 缓存装饰器实例,减少创建开销。
- 与其他模式结合:
- 适配器模式:装饰器增强功能,适配器转换接口。
- 代理模式:装饰器关注功能扩展,代理关注访问控制。
- 工厂模式:动态创建装饰器。
- 常见问题解决:
- 功能重复:检查装饰器是否重复实现功能。
- 异常处理:装饰器需捕获并处理组件异常。
- 线程安全:Servlet 环境中,装饰器需考虑多线程访问。
7. 与其他模式的区别
特性 | 装饰器模式 | 适配器模式 | 代理模式 |
---|---|---|---|
目的 | 动态增强功能 | 转换接口兼容 | 控制对象访问 |
实现方式 | 组合,包装对象 | 组合或继承 | 组合或继承 |
关注点 | 功能扩展 | 接口兼容 | 访问控制 |
接口一致性 | 与原对象相同 | 转换到新接口 | 与原对象相同 |
8. 总结
- 装饰器模式通过包装对象动态添加功能,适合需要灵活扩展的场景。
- 核心是组件接口、具体组件和装饰器,保持接口一致性。
- 在 Servlet 中,可用于增强请求或响应处理(如日志、权限)。
- 优点是动态性和符合开闭原则,缺点是复杂性和类数量增加。
- 注意中文编码、装饰顺序和线程安全。
如果需要更复杂的示例(如多层装饰、权限检查)、Servlet 集成细节,或与其他模式(如适配器、代理)的深入对比,请告诉我!