装饰器模式
大约 10 分钟
装饰器模式
什么是装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许你动态地给对象添加新的功能,同时又不改变其现有的结构。装饰器模式通过创建一个包装对象,也就是装饰器,来包裹真实的对象。
装饰器模式的核心思想是:
- 动态地给对象添加新的功能
- 不改变对象原有的结构和功能
- 通过包装对象来实现功能扩展
为什么需要装饰器模式
在面向对象设计中,我们经常需要给对象添加新的功能。传统的继承方式虽然可以实现功能扩展,但会导致类的膨胀和设计的僵化。装饰器模式提供了一种更加灵活的解决方案:
- 避免类爆炸:通过继承扩展功能会导致类的数量急剧增加
- 动态扩展:可以在运行时动态地添加或删除功能
- 符合开闭原则:对扩展开放,对修改封闭
装饰器模式的结构
装饰器模式包含以下几个角色:
- 组件(Component):定义一个对象接口,可以给这些对象动态地添加职责
- 具体组件(ConcreteComponent):定义一个对象,可以给这个对象添加一些职责
- 装饰器(Decorator):维持一个指向Component对象的引用,并定义一个与Component接口一致的接口
- 具体装饰器(ConcreteDecorator):向组件添加职责
装饰器模式的实现
基本实现
// 组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("具体组件的操作");
}
}
// 装饰器抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("具体装饰器A添加的行为");
}
}
// 具体装饰器B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
System.out.println("具体装饰器B添加的行为");
}
private void addedBehavior() {
System.out.println("具体装饰器B添加的行为");
}
}
// 使用示例
public class DecoratorDemo {
public static void main(String[] args) {
// 创建具体组件
Component component = new ConcreteComponent();
System.out.println("基本操作:");
component.operation();
System.out.println("\n添加装饰器A:");
Component decoratorA = new ConcreteDecoratorA(component);
decoratorA.operation();
System.out.println("\n添加装饰器B:");
Component decoratorB = new ConcreteDecoratorB(component);
decoratorB.operation();
System.out.println("\n同时添加装饰器A和B:");
Component decoratorAB = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
decoratorAB.operation();
}
}
实际应用示例
// 咖啡店示例
// 咖啡接口
interface Coffee {
String getDescription();
double getCost();
}
// 基础咖啡实现
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "简单咖啡";
}
@Override
public double getCost() {
return 2.0;
}
}
// 咖啡装饰器抽象类
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double getCost() {
return coffee.getCost();
}
}
// 牛奶装饰器
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", 牛奶";
}
@Override
public double getCost() {
return coffee.getCost() + 0.5;
}
}
// 糖装饰器
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", 糖";
}
@Override
public double getCost() {
return coffee.getCost() + 0.3;
}
}
// 巧克力装饰器
class ChocolateDecorator extends CoffeeDecorator {
public ChocolateDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", 巧克力";
}
@Override
public double getCost() {
return coffee.getCost() + 0.8;
}
}
// 奶泡装饰器
class WhipDecorator extends CoffeeDecorator {
public WhipDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + ", 奶泡";
}
@Override
public double getCost() {
return coffee.getCost() + 0.7;
}
}
// 使用示例
public class CoffeeShopDemo {
public static void main(String[] args) {
// 简单咖啡
Coffee coffee = new SimpleCoffee();
System.out.println("订单: " + coffee.getDescription());
System.out.println("价格: $" + coffee.getCost());
System.out.println();
// 加牛奶的咖啡
Coffee milkCoffee = new MilkDecorator(coffee);
System.out.println("订单: " + milkCoffee.getDescription());
System.out.println("价格: $" + milkCoffee.getCost());
System.out.println();
// 加牛奶和糖的咖啡
Coffee milkSugarCoffee = new SugarDecorator(new MilkDecorator(coffee));
System.out.println("订单: " + milkSugarCoffee.getDescription());
System.out.println("价格: $" + milkSugarCoffee.getCost());
System.out.println();
// 复杂的咖啡:牛奶 + 糖 + 巧克力 + 奶泡
Coffee complexCoffee = new WhipDecorator(
new ChocolateDecorator(
new SugarDecorator(
new MilkDecorator(coffee)
)
)
);
System.out.println("订单: " + complexCoffee.getDescription());
System.out.println("价格: $" + complexCoffee.getCost());
}
}
装饰器模式的应用场景
- 动态添加功能:当需要动态地给一个对象添加功能,这些功能也可以动态地被撤销时
- 避免子类扩展:当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时
- Java I/O流:Java的I/O流系统大量使用了装饰器模式
- 图形界面组件:为组件添加滚动条、边框等装饰
- Web开发:为HTTP请求和响应添加中间件处理
装饰器模式的优缺点
优点
- 扩展性好:装饰器模式相比继承可以提供更多的灵活性
- 符合开闭原则:对扩展开放,对修改封闭
- 可以动态地添加或删除功能:可以在运行时动态地添加或删除功能
- 可以通过使用不同的装饰器类来创建出功能不同的对象
缺点
- 产生很多小对象:使用装饰器模式会产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或是它们的属性值有所不同
- 调试困难:由于调试时需要逐层调用,因此调试比较困难
- 设计复杂:比继承更加易于出错,对于初学者来说,使用装饰器模式进行设计往往需要充分考虑组件和装饰器之间的关系
装饰器模式与其他模式的比较
与适配器模式的区别
- 装饰器模式:不改变接口,但增加对象的责任
- 适配器模式:将一个接口转换成另一个接口
与桥接模式的区别
- 装饰器模式:关注于动态地给对象添加职责
- 桥接模式:关注于如何将抽象与实现分离
与组合模式的区别
- 装饰器模式:关注于给对象添加职责
- 组合模式:关注于对象的组织结构
装饰器模式的变体
1. 带验证的装饰器模式
// 带验证的装饰器
abstract class ValidatingDecorator extends CoffeeDecorator {
public ValidatingDecorator(Coffee coffee) {
super(coffee);
}
// 验证方法
protected void validate() {
if (coffee == null) {
throw new IllegalArgumentException("咖啡对象不能为空");
}
}
@Override
public String getDescription() {
validate();
return super.getDescription();
}
@Override
public double getCost() {
validate();
return super.getCost();
}
}
2. 带日志的装饰器模式
// 带日志的装饰器
class LoggingDecorator extends CoffeeDecorator {
public LoggingDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
String description = coffee.getDescription();
System.out.println("获取描述: " + description);
return description;
}
@Override
public double getCost() {
double cost = coffee.getCost();
System.out.println("获取价格: $" + cost);
return cost;
}
}
实际项目中的应用
// Java I/O流装饰器模式示例
// 模拟Java I/O流的设计
// 输入流接口
interface InputStream {
int read() throws java.io.IOException;
void close() throws java.io.IOException;
}
// 基础文件输入流
class FileInputStream implements InputStream {
private String fileName;
public FileInputStream(String fileName) {
this.fileName = fileName;
System.out.println("打开文件: " + fileName);
}
@Override
public int read() throws java.io.IOException {
// 模拟读取数据
System.out.println("从文件读取数据");
return 1;
}
@Override
public void close() throws java.io.IOException {
System.out.println("关闭文件: " + fileName);
}
}
// 缓冲输入流装饰器
class BufferedInputStream implements InputStream {
private InputStream inputStream;
private int bufferSize;
public BufferedInputStream(InputStream inputStream) {
this(inputStream, 8192); // 默认缓冲区大小
}
public BufferedInputStream(InputStream inputStream, int bufferSize) {
this.inputStream = inputStream;
this.bufferSize = bufferSize;
System.out.println("添加缓冲区,大小: " + bufferSize + " 字节");
}
@Override
public int read() throws java.io.IOException {
System.out.println("通过缓冲区读取数据");
return inputStream.read();
}
@Override
public void close() throws java.io.IOException {
inputStream.close();
}
}
// 数据输入流装饰器
class DataInputStream implements InputStream {
private InputStream inputStream;
public DataInputStream(InputStream inputStream) {
this.inputStream = inputStream;
System.out.println("添加数据读取功能");
}
@Override
public int read() throws java.io.IOException {
System.out.println("读取数据类型");
return inputStream.read();
}
// 添加读取特定数据类型的方法
public int readInt() throws java.io.IOException {
System.out.println("读取整数数据");
return 42; // 模拟返回值
}
public String readUTF() throws java.io.IOException {
System.out.println("读取UTF字符串");
return "Hello World"; // 模拟返回值
}
@Override
public void close() throws java.io.IOException {
inputStream.close();
}
}
// 使用示例
public class StreamDecoratorDemo {
public static void main(String[] args) {
try {
// 基础文件输入流
System.out.println("=== 基础文件输入流 ===");
InputStream fileStream = new FileInputStream("test.txt");
fileStream.read();
fileStream.close();
System.out.println("\n=== 带缓冲的文件输入流 ===");
InputStream bufferedStream = new BufferedInputStream(new FileInputStream("test.txt"));
bufferedStream.read();
bufferedStream.close();
System.out.println("\n=== 带数据读取功能的缓冲文件输入流 ===");
DataInputStream dataStream = new DataInputStream(
new BufferedInputStream(new FileInputStream("test.txt"))
);
dataStream.read();
dataStream.readInt();
dataStream.readUTF();
dataStream.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
// Web请求处理装饰器示例
// HTTP请求接口
interface HttpRequest {
String getUrl();
String getMethod();
String getHeader(String name);
String getBody();
}
// HTTP响应接口
interface HttpResponse {
int getStatusCode();
String getHeader(String name);
String getBody();
}
// 基础HTTP请求实现
class BasicHttpRequest implements HttpRequest {
private String url;
private String method;
private Map<String, String> headers;
private String body;
public BasicHttpRequest(String url, String method) {
this.url = url;
this.method = method;
this.headers = new HashMap<>();
}
@Override
public String getUrl() {
return url;
}
@Override
public String getMethod() {
return method;
}
@Override
public String getHeader(String name) {
return headers.get(name);
}
@Override
public String getBody() {
return body;
}
public void setHeader(String name, String value) {
headers.put(name, value);
}
public void setBody(String body) {
this.body = body;
}
}
// 请求处理器接口
interface RequestHandler {
HttpResponse handle(HttpRequest request);
}
// 基础请求处理器
class BasicRequestHandler implements RequestHandler {
@Override
public HttpResponse handle(HttpRequest request) {
System.out.println("处理请求: " + request.getMethod() + " " + request.getUrl());
// 返回模拟响应
return new BasicHttpResponse(200, "OK");
}
}
// 基础HTTP响应实现
class BasicHttpResponse implements HttpResponse {
private int statusCode;
private String body;
private Map<String, String> headers;
public BasicHttpResponse(int statusCode, String body) {
this.statusCode = statusCode;
this.body = body;
this.headers = new HashMap<>();
}
@Override
public int getStatusCode() {
return statusCode;
}
@Override
public String getHeader(String name) {
return headers.get(name);
}
@Override
public String getBody() {
return body;
}
public void setHeader(String name, String value) {
headers.put(name, value);
}
}
// 请求处理器装饰器抽象类
abstract class RequestHandlerDecorator implements RequestHandler {
protected RequestHandler handler;
public RequestHandlerDecorator(RequestHandler handler) {
this.handler = handler;
}
@Override
public HttpResponse handle(HttpRequest request) {
return handler.handle(request);
}
}
// 日志装饰器
class LoggingDecorator implements RequestHandler {
private RequestHandler handler;
public LoggingDecorator(RequestHandler handler) {
this.handler = handler;
}
@Override
public HttpResponse handle(HttpRequest request) {
System.out.println("=== 请求开始 ===");
System.out.println("时间: " + new java.util.Date());
System.out.println("URL: " + request.getUrl());
System.out.println("方法: " + request.getMethod());
HttpResponse response = handler.handle(request);
System.out.println("响应状态: " + response.getStatusCode());
System.out.println("=== 请求结束 ===");
return response;
}
}
// 认证装饰器
class AuthenticationDecorator implements RequestHandler {
private RequestHandler handler;
public AuthenticationDecorator(RequestHandler handler) {
this.handler = handler;
}
@Override
public HttpResponse handle(HttpRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
System.out.println("认证失败: 缺少或无效的认证头");
return new BasicHttpResponse(401, "Unauthorized");
}
System.out.println("认证成功");
return handler.handle(request);
}
}
// 缓存装饰器
class CachingDecorator implements RequestHandler {
private RequestHandler handler;
private Map<String, HttpResponse> cache = new HashMap<>();
public CachingDecorator(RequestHandler handler) {
this.handler = handler;
}
@Override
public HttpResponse handle(HttpRequest request) {
String cacheKey = request.getMethod() + ":" + request.getUrl();
// 检查缓存
if (cache.containsKey(cacheKey)) {
System.out.println("从缓存返回响应");
return cache.get(cacheKey);
}
// 处理请求并缓存结果
HttpResponse response = handler.handle(request);
cache.put(cacheKey, response);
System.out.println("缓存响应");
return response;
}
}
// 使用示例
public class WebDecoratorDemo {
public static void main(String[] args) {
// 创建基础处理器
RequestHandler handler = new BasicRequestHandler();
// 添加日志功能
handler = new LoggingDecorator(handler);
// 添加认证功能
handler = new AuthenticationDecorator(handler);
// 添加缓存功能
handler = new CachingDecorator(handler);
// 创建请求
BasicHttpRequest request1 = new BasicHttpRequest("/api/users", "GET");
request1.setHeader("Authorization", "Bearer token123");
BasicHttpRequest request2 = new BasicHttpRequest("/api/users", "GET");
request2.setHeader("Authorization", "Bearer token123");
// 处理请求
System.out.println("第一次请求:");
handler.handle(request1);
System.out.println("\n第二次请求(应该从缓存获取):");
handler.handle(request2);
// 无认证头的请求
System.out.println("\n无认证头的请求:");
BasicHttpRequest request3 = new BasicHttpRequest("/api/users", "GET");
handler.handle(request3);
}
}
总结
装饰器模式是一种非常实用的结构型设计模式,它通过创建包装对象来动态地给对象添加新的功能,而不需要修改原有对象的结构。在实际开发中,当我们需要动态地扩展对象功能时,装饰器模式是一个很好的选择。
使用装饰器模式的关键点:
- 识别出需要动态扩展功能的对象
- 定义统一的组件接口
- 创建装饰器类来包装组件对象
- 通过组合方式将装饰器层层包装
装饰器模式的优点是可以动态地添加或删除功能,符合开闭原则,但也需要注意可能会产生很多小对象,增加系统的复杂性。在现代Java开发中,装饰器模式广泛应用于I/O流处理、Web中间件、图形界面组件等领域。