深入理解设计模式之装饰器模式
深入理解设计模式之装饰器模式:动态扩展对象功能的优雅方案
1. 装饰器模式概述
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象添加新的功能,同时不改变其结构。装饰器模式通过将对象包装在装饰器类中,以透明的方式动态地扩展对象的行为,提供了比继承更加灵活的替代方案。
装饰器模式遵循开闭原则,允许系统在不修改现有代码的情况下引入新功能。它通过组合而非继承来扩展对象功能,避免了继承带来的类爆炸问题。
2. 装饰器模式的结构
装饰器模式包含以下主要角色:
- 组件接口(Component): 定义了被装饰对象和装饰器的公共接口
- 具体组件(ConcreteComponent): 被装饰的原始对象,定义了需要添加功能的对象
- 装饰器抽象类(Decorator): 维护对组件对象的引用,并实现与组件接口一致的接口
- 具体装饰器(ConcreteDecorator): 向组件添加新的功能
3. 基本实现
下面是装饰器模式的基本实现示例:
// 组件接口
interface Component {void operation();
}// 具体组件
class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("ConcreteComponent: Basic operation");}
}// 装饰器抽象类
abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}// 具体装饰器A
class ConcreteDecoratorA extends Decorator {public ConcreteDecoratorA(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedBehavior();}private void addedBehavior() {System.out.println("ConcreteDecoratorA: Added behavior");}
}// 具体装饰器B
class ConcreteDecoratorB extends Decorator {public ConcreteDecoratorB(Component component) {super(component);}@Overridepublic void operation() {super.operation();addedBehavior();}private void addedBehavior() {System.out.println("ConcreteDecoratorB: Added behavior");}
}// 客户端代码
public class BasicDecoratorDemo {public static void main(String[] args) {// 创建具体组件Component component = new ConcreteComponent();// 用装饰器A装饰Component decoratedA = new ConcreteDecoratorA(component);// 用装饰器B装饰已经被装饰器A装饰过的对象Component decoratedB = new ConcreteDecoratorB(decoratedA);// 调用方法,执行多层装饰的行为System.out.println("Original component:");component.operation();System.out.println("\nDecorated with A:");decoratedA.operation();System.out.println("\nDecorated with A and B:");decoratedB.operation();}
}
4. 实际应用场景
4.1 咖啡订购系统
// 组件接口:饮料
interface Beverage {String getDescription();double cost();
}// 具体组件:各种饮料
class Espresso implements Beverage {@Overridepublic String getDescription() {return "Espresso";}@Overridepublic double cost() {return 1.99;}
}class HouseBlend implements Beverage {@Overridepublic String getDescription() {return "House Blend Coffee";}@Overridepublic double cost() {return 0.89;}
}class DarkRoast implements Beverage {@Overridepublic String getDescription() {return "Dark Roast Coffee";}@Overridepublic double cost() {return 0.99;}
}// 装饰器抽象类:调料
abstract class CondimentDecorator implements Beverage {protected Beverage beverage;public CondimentDecorator(Beverage beverage) {this.beverage = beverage;}public abstract String getDescription();
}// 具体装饰器:各种调料
class Mocha extends CondimentDecorator {public Mocha(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", Mocha";}@Overridepublic double cost() {return beverage.cost() + 0.20;}
}class Whip extends CondimentDecorator {public Whip(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", Whip";}@Overridepublic double cost() {return beverage.cost() + 0.10;}
}class Soy extends CondimentDecorator {public Soy(Beverage beverage) {super(beverage);}@Overridepublic String getDescription() {return beverage.getDescription() + ", Soy";}@Overridepublic double cost() {return beverage.cost() + 0.15;}
}// 客户端代码
public class CoffeeShopDemo {public static void main(String[] args) {// 订购一杯EspressoBeverage beverage1 = new Espresso();System.out.println(beverage1.getDescription() + " $" + beverage1.cost());// 订购一杯DarkRoast,加Mocha、Mocha和WhipBeverage beverage2 = new DarkRoast();beverage2 = new Mocha(beverage2);beverage2 = new Mocha(beverage2);beverage2 = new Whip(beverage2);System.out.println(beverage2.getDescription() + " $" + beverage2.cost());// 订购一杯HouseBlend,加Soy、Mocha和WhipBeverage beverage3 = new HouseBlend();beverage3 = new Soy(beverage3);beverage3 = new Mocha(beverage3);beverage3 = new Whip(beverage3);System.out.println(beverage3.getDescription() + " $" + beverage3.cost());}
}
4.2 数据源组件
// 数据源接口
interface DataSource {void writeData(String data);String readData();
}// 基础文件数据源
class FileDataSource implements DataSource {private String filename;public FileDataSource(String filename) {this.filename = filename;}@Overridepublic void writeData(String data) {System.out.println("Writing data to file: " + filename);// 实际实现会将数据写入文件System.out.println("Data: " + data);}@Overridepublic String readData() {System.out.println("Reading data from file: " + filename);// 实际实现会从文件读取数据return "Some data from file";}
}// 装饰器基类
abstract class DataSourceDecorator implements DataSource {protected DataSource wrappee;public DataSourceDecorator(DataSource source) {this.wrappee = source;}@Overridepublic void writeData(String data) {wrappee.writeData(data);}@Overridepublic String readData() {return wrappee.readData();}
}// 加密装饰器
class EncryptionDecorator extends DataSourceDecorator {public EncryptionDecorator(DataSource source) {super(source);}@Overridepublic void writeData(String data) {System.out.println("Encrypting data...");String encryptedData = encrypt(data);wrappee.writeData(encryptedData);}@Overridepublic String readData() {String encryptedData = wrappee.readData();System.out.println("Decrypting data...");return decrypt(encryptedData);}private String encrypt(String data) {// 简单的模拟加密过程return "ENCRYPTED[" + data + "]";}private String decrypt(String data) {// 简单的模拟解密过程if (data.startsWith("ENCRYPTED[") && data.endsWith("]")) {return data.substring(10, data.length() - 1);}return data;}
}// 压缩装饰器
class CompressionDecorator extends DataSourceDecorator {public CompressionDecorator(DataSource source) {super(source);}@Overridepublic void writeData(String data) {System.out.println("Compressing data...");String compressedData = compress(data);wrappee.writeData(compressedData);}@Overridepublic String readData() {String compressedData = wrappee.readData();System.out.println("Decompressing data...");return decompress(compressedData);}private String compress(String data) {// 简单的模拟压缩过程return "COMPRESSED[" + data + "]";}private String decompress(String data) {// 简单的模拟解压过程if (data.startsWith("COMPRESSED[") && data.endsWith("]")) {return data.substring(11, data.length() - 1);}return data;}
}// 缓存装饰器
class CachingDecorator extends DataSourceDecorator {private String cache;public CachingDecorator(DataSource source) {super(source);}@Overridepublic void writeData(String data) {wrappee.writeData(data);cache = data;System.out.println("Data cached: " + data);}@Overridepublic String readData() {if (cache != null) {System.out.println("Reading from cache");return cache;}return wrappee.readData();}
}// 客户端代码
public class DataSourceDemo {public static void main(String[] args) {// 原始数据源DataSource source = new FileDataSource("data.txt");// 添加加密功能DataSource encrypted = new EncryptionDecorator(source);// 添加压缩功能DataSource compressedEncrypted = new CompressionDecorator(encrypted);// 添加缓存功能DataSource cachedCompressedEncrypted = new CachingDecorator(compressedEncrypted);// 写入数据,经过多层处理System.out.println("--- Writing data with multiple decorators ---");cachedCompressedEncrypted.writeData("Hello World");// 读取数据,经过多层处理System.out.println("\n--- Reading data with multiple decorators ---");String data = cachedCompressedEncrypted.readData();System.out.println("Final data: " + data);// 再次读取(应该从缓存中获取)System.out.println("\n--- Reading data again (should use cache) ---");data = cachedCompressedEncrypted.readData();System.out.println("Final data: " + data);}
}
5. Java I/O库中的装饰器模式
Java I/O库是装饰器模式的经典应用,下面是一个使用Java I/O库的示例:
import java.io.*;public class JavaIODecoratorExample {public static void main(String[] args) {try {// 创建一个示例文件String fileName = "example.txt";FileOutputStream fos = new FileOutputStream(fileName);fos.write("Hello, Decorator Pattern!".getBytes());fos.close();// 使用装饰器模式读取文件System.out.println("Reading file using decorators:");// 基本组件:文件输入流InputStream fileInputStream = new FileInputStream(fileName);// 添加缓冲功能的装饰器InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);// 添加数据处理功能的装饰器DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);// 读取数据byte[] buffer = new byte[1024];int bytesRead = dataInputStream.read(buffer);String content = new String(buffer, 0, bytesRead);System.out.println("Content: " + content);// 关闭流dataInputStream.close();// 说明装饰器层次System.out.println("\nDecorator hierarchy:");System.out.println("1. FileInputStream (Concrete Component)");System.out.println("2. BufferedInputStream (Decorator) -> adds buffering functionality");System.out.println("3. DataInputStream (Decorator) -> adds data reading functionality");// 创建带缓冲和行号的装饰读取器System.out.println("\nReading file with LineNumberReader (another decorator example):");LineNumberReader lineReader = new LineNumberReader(new FileReader(fileName));String line;while ((line = lineReader.readLine()) != null) {System.out.println("Line " + lineReader.getLineNumber() + ": " + line);}lineReader.close();} catch (IOException e) {e.printStackTrace();}}
}
6. 自定义Java I/O装饰器
让我们创建一个自定义的I/O装饰器,用于在读取文本文件时将所有文本转换为大写:
import java.io.*;// 自定义输入流装饰器
class UpperCaseInputStream extends FilterInputStream {public UpperCaseInputStream(InputStream in) {super(in);}@Overridepublic int read() throws IOException {int c = super.read();return (c == -1) ? c : Character.toUpperCase((char) c);}@Overridepublic int read(byte[] b, int offset, int len) throws IOException {int result = super.read(b, offset, len);for (int i = offset; i < offset + result; i++) {b[i] = (byte) Character.toUpperCase((char) b[i]);}return result;}
}// 自定义Reader装饰器
class UpperCaseReader extends FilterReader {public UpperCaseReader(Reader in) {super(in);}@Overridepublic int read() throws IOException {int c = super.read();return (c == -1) ? c : Character.toUpperCase((char) c);}@Overridepublic int read(char[] cbuf, int offset, int length) throws IOException {int result = super.read(cbuf, offset, length);for (int i = offset; i < offset + result; i++) {cbuf[i] = Character.toUpperCase(cbuf[i]);}return result;}
}public class CustomIODecoratorDemo {public static void main(String[] args) {try {// 创建一个示例文件String fileName = "lowercase.txt";FileWriter writer = new FileWriter(fileName);writer.write("this is a test of custom io decorators.\nthey should convert all text to uppercase.");writer.close();System.out.println("--- Using UpperCaseInputStream ---");// 使用自定义InputStream装饰器try (InputStream fis = new FileInputStream(fileName);InputStream ucis = new UpperCaseInputStream(fis);BufferedReader reader = new BufferedReader(new InputStreamReader(ucis))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}System.out.println("\n--- Using UpperCaseReader ---");// 使用自定义Reader装饰器try (Reader fileReader = new FileReader(fileName);Reader ucReader = new UpperCaseReader(fileReader);BufferedReader bufferedReader = new BufferedReader(ucReader)) {String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}}} catch (IOException e) {e.printStackTrace();}}
}
7. 装饰器模式与Web应用中的Servlet过滤器
Java Web开发中的Servlet过滤器也是装饰器模式的应用:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;// 日志过滤器
public class LoggingFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化过滤器}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;// 请求前处理System.out.println("Logging request: " + httpRequest.getMethod() + " " + httpRequest.getRequestURI());// 传递请求给下一个过滤器或目标资源chain.doFilter(request, response);// 响应后处理System.out.println("Logging response from: " + httpRequest.getRequestURI());}@Overridepublic void destroy() {// 清理资源}
}// 认证过滤器
public class AuthenticationFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化过滤器}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;// 检查用户是否已认证HttpSession session = httpRequest.getSession(false);if (session == null || session.getAttribute("user") == null) {// 重定向到登录页面httpResponse.sendRedirect(httpRequest.getContextPath() + "/login.jsp");return;}// 用户已认证,继续请求链chain.doFilter(request, response);}@Overridepublic void destroy() {// 清理资源}
}// 性能监控过滤器
public class PerformanceFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化过滤器}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;// 开始计时long startTime = System.currentTimeMillis();// 继续请求链chain.doFilter(request, response);// 计算处理时间long endTime = System.currentTimeMillis();System.out.println("Request to " + httpRequest.getRequestURI() + " took " + (endTime - startTime) + " ms");}@Overridepublic void destroy() {// 清理资源}
}// 在web.xml中配置过滤器链
/*
<filter><filter-name>LoggingFilter</filter-name><filter-class>com.example.LoggingFilter</filter-class>
</filter>
<filter><filter-name>AuthenticationFilter</filter-name><filter-class>com.example.AuthenticationFilter</filter-class>
</filter>
<filter><filter-name>PerformanceFilter</filter-name><filter-class>com.example.PerformanceFilter</filter-class>
</filter><filter-mapping><filter-name>LoggingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping><filter-name>AuthenticationFilter</filter-name><url-pattern>/secured/*</url-pattern>
</filter-mapping>
<filter-mapping><filter-name>PerformanceFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
*/
8. 装饰器模式的优缺点
优点
- 更加灵活的扩展功能:相比继承,装饰器提供了更灵活的方式来扩展对象功能
- 遵循开闭原则:不修改现有代码即可添加新功能
- 组合优于继承:使用对象组合而非继承,避免了类爆炸问题
- 动态添加功能:可以在运行时动态地给对象添加功能
- 职责单一:每个装饰器专注于一项特定功能,符合单一职责原则
缺点
- 多层装饰器复杂性:使用多层装饰可能导致系统复杂度增加
- 装饰顺序的依赖:有些情况下装饰顺序会影响结果
- 可能产生过多的小类:每个装饰功能都需要一个装饰器类
- 难以从外部识别对象类型:装饰过的对象可能难以识别真实类型
9. 适用场景
装饰器模式适用于以下场景:
- 不影响其他对象的情况下,动态地给对象添加职责
- 对象的职责可以动态地撤销
- 当不能采用继承扩展功能时(可能因为最终类或继承层次过深)
- 当系统需要动态地组合功能时
- 需要为系统的不同部分提供不同功能组合时
10. 装饰器模式与其他模式的比较
装饰器 vs 适配器
- 目的不同:
- 装饰器:不改变接口,但添加责任
- 适配器:转换接口,使不兼容的接口能够一起工作
装饰器 vs 组合
- 结构相似但目的不同:
- 装饰器:动态地添加功能
- 组合:创建树形结构,统一处理简单和复杂对象
装饰器 vs 策略
- 侧重点不同:
- 装饰器:添加功能
- 策略:替换算法
11. 总结
装饰器模式是一种强大而灵活的设计模式,它允许我们在不修改现有代码的情况下动态地扩展对象的功能。通过使用对象组合而非继承,装饰器模式为系统提供了更大的灵活性,同时也避免了继承带来的类爆炸问题。
Java I/O库是装饰器模式的典型应用,通过组合不同的流装饰器,我们可以构建具有多种功能的复杂I/O处理管道。在Web开发中,Servlet过滤器也采用了装饰器模式的思想,允许我们为请求和响应添加各种处理功能。
在实际应用中,装饰器模式特别适合需要动态组合多种功能的场景,它使得我们可以按需组合功能,而不必创建大量的子类。然而,我们也需要注意避免过度使用装饰器导致系统变得复杂难以理解。
掌握装饰器模式,是成为优秀软件设计师的重要一步,它代表了面向对象设计中"组合优于继承"的重要原则。