装饰器模式详解
大约 12 分钟约 3524 字
装饰器模式详解
简介
装饰器模式(Decorator Pattern)是一种结构型设计模式,允许在不修改原始对象代码的情况下,通过将对象放入包含行为的特殊封装对象中来动态地添加新功能。在 C# 中,装饰器模式广泛应用于 .NET 类库(如 Stream 的继承体系)、ASP.NET Core 中间件管道,以及依赖注入中的装饰器注册。
特点
基础装饰器模式实现
咖啡订单系统示例
// 抽象组件
public abstract class Beverage
{
public string Description { get; protected set; } = "未知饮品";
public abstract decimal Cost();
public override string ToString() => $"{Description} - {Cost():C}";
}
// 具体组件
public class Espresso : Beverage
{
public Espresso() => Description = "浓缩咖啡";
public override decimal Cost() => 15.0m;
}
public class DarkRoast : Beverage
{
public DarkRoast() => Description = "深度烘焙咖啡";
public override decimal Cost() => 12.0m;
}
public class Decaf : Beverage
{
public Decaf() => Description = "低咖啡因咖啡";
public override decimal Cost() => 13.0m;
}
// 装饰器基类
public abstract class CondimentDecorator : Beverage
{
protected Beverage _beverage;
protected CondimentDecorator(Beverage beverage) => _beverage = beverage;
}
// 具体装饰器
public class Milk : CondimentDecorator
{
public Milk(Beverage beverage) : base(beverage)
{
Description = $"{_beverage.Description} + 牛奶";
}
public override decimal Cost() => _beverage.Cost() + 3.0m;
}
public class Mocha : CondimentDecorator
{
public Mocha(Beverage beverage) : base(beverage)
{
Description = $"{_beverage.Description} + 摩卡";
}
public override decimal Cost() => _beverage.Cost() + 4.0m;
}
public class Whip : CondimentDecorator
{
public Whip(Beverage beverage) : base(beverage)
{
Description = $"{_beverage.Description} + 奶油";
}
public override decimal Cost() => _beverage.Cost() + 2.5m;
}
public class Soy : CondimentDecorator
{
public Soy(Beverage beverage) : base(beverage)
{
Description = $"{_beverage.Description} + 豆奶";
}
public override decimal Cost() => _beverage.Cost() + 3.5m;
}
// 使用 - 可以任意组合装饰器
var beverage = new Espresso();
Console.WriteLine(beverage); // 浓缩咖啡 - ¥15.00
var doubleMochaWhip = new Whip(new Mocha(new Mocha(new DarkRoast())));
Console.WriteLine(doubleMochaWhip); // 深度烘焙咖啡 + 摩卡 + 摩卡 + 奶油 - ¥22.50Stream 装饰器体系
.NET 中 Stream 类的继承体系是装饰器模式最经典的应用之一。
// Stream 是抽象组件
// FileStream, MemoryStream 是具体组件
// BufferedStream, CryptoStream, GZipStream 是装饰器
// 基础流 - 具体组件
using var fileStream = new FileStream("data.bin", FileMode.Create);
// 用 BufferedStream 装饰 - 添加缓冲功能
using var bufferedStream = new BufferedStream(fileStream, 8192);
// 用 GZipStream 装饰 - 添加压缩功能
using var gzipStream = new GZipStream(bufferedStream, CompressionLevel.Optimal);
// 用 CryptoStream 装饰 - 添加加密功能
using var aes = Aes.Create();
aes.Key = Convert.FromBase64String("你的Base64密钥");
aes.IV = Convert.FromBase64String("你的Base64IV");
using var cryptoTransform = aes.CreateEncryptor();
using var cryptoStream = new CryptoStream(gzipStream, cryptoTransform, CryptoStreamMode.Write);
// 现在写入 cryptoStream 的数据会自动:加密 -> 压缩 -> 缓冲 -> 写入文件
var data = System.Text.Encoding.UTF8.GetBytes("这是一段需要加密和压缩的敏感数据");
await cryptoStream.WriteAsync(data);自定义 Stream 装饰器
// 自定义流装饰器 - 统计读写字节数
public class StatisticsStream : Stream
{
private readonly Stream _innerStream;
private long _bytesRead;
private long _bytesWritten;
public StatisticsStream(Stream innerStream)
{
_innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
}
public long BytesRead => _bytesRead;
public long BytesWritten => _bytesWritten;
public override int Read(byte[] buffer, int offset, int count)
{
var bytesRead = _innerStream.Read(buffer, offset, count);
_bytesRead += bytesRead;
return bytesRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
_innerStream.Write(buffer, offset, count);
_bytesWritten += count;
}
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default)
{
var bytesRead = await _innerStream.ReadAsync(buffer, offset, count, cancellationToken);
_bytesRead += bytesRead;
return bytesRead;
}
public override async Task WriteAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default)
{
await _innerStream.WriteAsync(buffer, offset, count, cancellationToken);
_bytesWritten += count;
}
// 委托其他成员到内部流
public override bool CanRead => _innerStream.CanRead;
public override bool CanSeek => _innerStream.CanSeek;
public override bool CanWrite => _innerStream.CanWrite;
public override long Length => _innerStream.Length;
public override long Position
{
get => _innerStream.Position;
set => _innerStream.Position = value;
}
public override void Flush() => _innerStream.Flush();
public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
public override void SetLength(long value) => _innerStream.SetLength(value);
}中间件管道模式
ASP.NET Core 的中间件管道本质上是装饰器模式的一种变体,每个中间件都可以在请求前后添加自己的处理逻辑。
// 自定义中间件 - 本质是对 HttpContext 的装饰
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
var requestId = Guid.NewGuid().ToString("N")[..8];
// 在请求前添加逻辑
_logger.LogInformation("[{RequestId}] {Method} {Path} 开始处理",
requestId, context.Request.Method, context.Request.Path);
try
{
// 调用下一个中间件
await _next(context);
}
finally
{
stopwatch.Stop();
// 在请求后添加逻辑
_logger.LogInformation(
"[{RequestId}] {Method} {Path} 完成 - {StatusCode} - {ElapsedMs}ms",
requestId, context.Request.Method, context.Request.Path,
context.Response.StatusCode, stopwatch.ElapsedMilliseconds);
}
}
}
// 自定义异常处理中间件
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (ValidationException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(new
{
Error = "验证失败",
Details = ex.Message
});
}
catch (NotFoundException ex)
{
context.Response.StatusCode = 404;
await context.Response.WriteAsJsonAsync(new { Error = ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "未处理的异常");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new { Error = "服务器内部错误" });
}
}
}
// 注册中间件 - 顺序决定装饰器嵌套顺序
var app = builder.Build();
app.UseMiddleware<ExceptionHandlingMiddleware>(); // 最外层
app.UseMiddleware<RequestLoggingMiddleware>(); // 第二层
app.UseAuthentication(); // 第三层
app.UseAuthorization(); // 第四层
app.MapControllers(); // 最内层DI 中的装饰器注册
.NET 依赖注入支持装饰器注册模式。
// 服务接口
public interface IDataService
{
Task<string> GetDataAsync(string key);
Task SetDataAsync(string key, string value);
}
// 核心实现
public class SqlDataService : IDataService
{
public async Task<string> GetDataAsync(string key)
{
Console.WriteLine($"[SQL] 查询数据: {key}");
await Task.Delay(50); // 模拟数据库查询
return $"数据_{key}";
}
public async Task SetDataAsync(string key, string value)
{
Console.WriteLine($"[SQL] 写入数据: {key} = {value}");
await Task.Delay(50);
}
}
// 缓存装饰器
public class CachedDataService : IDataService
{
private readonly IDataService _inner;
private readonly ConcurrentDictionary<string, string> _cache = new();
public CachedDataService(IDataService inner)
{
_inner = inner;
}
public async Task<string> GetDataAsync(string key)
{
if (_cache.TryGetValue(key, out var cached))
{
Console.WriteLine($"[缓存命中] {key}");
return cached;
}
var data = await _inner.GetDataAsync(key);
_cache[key] = data;
return data;
}
public async Task SetDataAsync(string key, string value)
{
await _inner.SetDataAsync(key, value);
_cache[key] = value; // 更新缓存
}
}
// 日志装饰器
public class LoggingDataService : IDataService
{
private readonly IDataService _inner;
private readonly ILogger<LoggingDataService> _logger;
public LoggingDataService(IDataService inner, ILogger<LoggingDataService> logger)
{
_inner = inner;
_logger = logger;
}
public async Task<string> GetDataAsync(string key)
{
_logger.LogInformation("获取数据: {Key}", key);
var sw = Stopwatch.StartNew();
try
{
return await _inner.GetDataAsync(key);
}
finally
{
_logger.LogInformation("获取数据完成: {Key}, 耗时: {Ms}ms", key, sw.ElapsedMilliseconds);
}
}
public async Task SetDataAsync(string key, string value)
{
_logger.LogInformation("设置数据: {Key} = {Value}", key, value);
await _inner.SetDataAsync(key, value);
}
}
// 注册装饰器链
builder.Services.AddScoped<IDataService>(sp =>
{
var inner = new SqlDataService();
var cached = new CachedDataService(inner);
var logger = new LoggingDataService(cached,
sp.GetRequiredService<ILogger<LoggingDataService>>());
return logger;
});
// 调用链:LoggingDataService -> CachedDataService -> SqlDataService装饰器模式高级应用
泛型装饰器基类
/// <summary>
/// 泛型装饰器基类 — 减少装饰器中的样板代码
/// 自动委托所有方法到内部对象
/// </summary>
public abstract class DecoratorBase<TInterface> : TInterface
where TInterface : class
{
protected readonly TInterface Inner;
protected DecoratorBase(TInterface inner)
{
Inner = inner ?? throw new ArgumentNullException(nameof(inner));
}
}
// 使用 DispatchProxy 实现自动委托
public class LoggingDecorator<TInterface> : DispatchProxy
where TInterface : class
{
private TInterface? _target;
private ILogger? _logger;
public static TInterface Create(TInterface target, ILogger logger)
{
var proxy = Create<TInterface, LoggingDecorator<TInterface>>();
((LoggingDecorator<TInterface>)proxy)._target = target;
((LoggingDecorator<TInterface>)proxy)._logger = logger;
return proxy;
}
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
var sw = Stopwatch.StartNew();
_logger?.LogInformation("调用 {Method}", targetMethod?.Name);
try
{
var result = targetMethod?.Invoke(_target, args);
_logger?.LogInformation("{Method} 完成,耗时 {Ms}ms",
targetMethod?.Name, sw.ElapsedMilliseconds);
return result;
}
catch (TargetInvocationException ex)
{
_logger?.LogError(ex.InnerException, "{Method} 异常", targetMethod?.Name);
throw ex.InnerException!;
}
}
}
// 使用 DispatchProxy 创建装饰器
var decorated = LoggingDecorator<IDataService>.Create(
new SqlDataService(),
logger);重试装饰器
/// <summary>
/// 重试装饰器 — 自动重试失败的操作
/// </summary>
public class RetryDataService : IDataService
{
private readonly IDataService _inner;
private readonly int _maxRetries;
private readonly TimeSpan _delay;
private readonly ILogger<RetryDataService> _logger;
public RetryDataService(
IDataService inner,
int maxRetries,
TimeSpan delay,
ILogger<RetryDataService> logger)
{
_inner = inner;
_maxRetries = maxRetries;
_delay = delay;
_logger = logger;
}
public async Task<string> GetDataAsync(string key)
{
return await ExecuteWithRetry(
() => _inner.GetDataAsync(key),
nameof(GetDataAsync));
}
public async Task SetDataAsync(string key, string value)
{
await ExecuteWithRetry(
() => _inner.SetDataAsync(key, value),
nameof(SetDataAsync));
}
private async Task<T> ExecuteWithRetry<T>(
Func<Task<T>> operation, string operationName)
{
Exception? lastException = null;
for (int attempt = 1; attempt <= _maxRetries; attempt++)
{
try
{
return await operation();
}
catch (Exception ex)
{
lastException = ex;
_logger.LogWarning(ex,
"{Operation} 第 {Attempt}/{Max} 次尝试失败",
operationName, attempt, _maxRetries);
if (attempt < _maxRetries)
await Task.Delay(_delay * attempt); // 线性退避
}
}
throw lastException!;
}
private async Task ExecuteWithRetry(
Func<Task> operation, string operationName)
{
await ExecuteWithRetry(async () =>
{
await operation();
return true;
}, operationName);
}
}断路器装饰器
/// <summary>
/// 断路器装饰器 — 在连续失败时短路请求
/// </summary>
public class CircuitBreakerDataService : IDataService
{
private readonly IDataService _inner;
private readonly int _failureThreshold;
private readonly TimeSpan _resetTimeout;
private int _failureCount;
private DateTime _lastFailureTime = DateTime.MinValue;
private readonly object _lock = new();
private enum CircuitState { Closed, Open, HalfOpen }
private CircuitState _state = CircuitState.Closed;
public CircuitBreakerDataService(
IDataService inner,
int failureThreshold,
TimeSpan resetTimeout)
{
_inner = inner;
_failureThreshold = failureThreshold;
_resetTimeout = resetTimeout;
}
public async Task<string> GetDataAsync(string key)
{
EnsureCircuitAllowsRequest();
try
{
var result = await _inner.GetDataAsync(key);
OnSuccess();
return result;
}
catch (Exception ex)
{
OnFailure();
throw;
}
}
public async Task SetDataAsync(string key, string value)
{
EnsureCircuitAllowsRequest();
try
{
await _inner.SetDataAsync(key, value);
OnSuccess();
}
catch
{
OnFailure();
throw;
}
}
private void EnsureCircuitAllowsRequest()
{
lock (_lock)
{
if (_state == CircuitState.Open)
{
if (DateTime.UtcNow - _lastFailureTime > _resetTimeout)
{
_state = CircuitState.HalfOpen; // 尝试恢复
}
else
{
throw new InvalidOperationException(
"断路器已打开,请求被拒绝");
}
}
}
}
private void OnSuccess()
{
lock (_lock)
{
_failureCount = 0;
_state = CircuitState.Closed;
}
}
private void OnFailure()
{
lock (_lock)
{
_failureCount++;
_lastFailureTime = DateTime.UtcNow;
if (_failureCount >= _failureThreshold)
{
_state = CircuitState.Open;
}
}
}
}事件发布装饰器
/// <summary>
/// 事件发布装饰器 — 在数据变更时发布领域事件
/// </summary>
public class EventPublishingDataService : IDataService
{
private readonly IDataService _inner;
private readonly IEventBus _eventBus;
public EventPublishingDataService(IDataService inner, IEventBus eventBus)
{
_inner = inner;
_eventBus = eventBus;
}
public async Task<string> GetDataAsync(string key)
{
return await _inner.GetDataAsync(key);
}
public async Task SetDataAsync(string key, string value)
{
// 获取旧值
var oldValue = await _inner.GetDataAsync(key);
// 执行更新
await _inner.SetDataAsync(key, value);
// 发布变更事件
await _eventBus.PublishAsync(new DataChangedEvent(
Key: key,
OldValue: oldValue,
NewValue: value,
ChangedAt: DateTime.UtcNow));
}
}
public record DataChangedEvent(
string Key, string? OldValue, string NewValue, DateTime ChangedAt);
public interface IEventBus
{
Task PublishAsync<TEvent>(TEvent event_);
}完整装饰器链注册
/// <summary>
/// 在 DI 容器中注册完整的装饰器链
/// </summary>
builder.Services.AddScoped<IDataService>(sp =>
{
var logger = sp.GetRequiredService<ILogger<LoggingDataService>>();
// 从内到外构建装饰器链
IDataService service = new SqlDataService();
// 第一层:断路器
service = new CircuitBreakerDataService(
service, failureThreshold: 3, resetTimeout: TimeSpan.FromSeconds(30));
// 第二层:重试
service = new RetryDataService(
service, maxRetries: 3, delay: TimeSpan.FromSeconds(1), logger: sp.GetRequiredService<ILogger<RetryDataService>>());
// 第三层:缓存
service = new CachedDataService(service);
// 第四层:事件发布
service = new EventPublishingDataService(
service, sp.GetRequiredService<IEventBus>());
// 第五层:日志(最外层)
service = new LoggingDataService(service, logger);
return service;
});
// 调用链:
// LoggingDataService
// -> EventPublishingDataService
// -> CachedDataService
// -> RetryDataService
// -> CircuitBreakerDataService
// -> SqlDataService装饰器模式与开闭原则
设计原则对照
装饰器模式体现的设计原则:
1. 开闭原则(OCP)— 对扩展开放,对修改关闭
新增功能只需添加新装饰器,无需修改已有代码
2. 单一职责原则(SRP)— 每个装饰器只关注一个关注点
LoggingDataService 只负责日志
CachedDataService 只负责缓存
RetryDataService 只负责重试
3. 依赖倒置原则(DIP)— 依赖抽象而非具体实现
所有装饰器都依赖 IDataService 接口
4. 组合优于继承 — 用对象组合代替类继承
功能通过组合装饰器实现,而非继承层次
与其他模式的对比:
- 代理模式 — 结构相同,但意图不同(代理控制访问,装饰器增加功能)
- 适配器模式 — 改变接口,装饰器保持接口不变
- 责任链模式 — 每个处理器决定是否传递,装饰器总是传递优点
缺点
总结
装饰器模式是动态扩展对象功能的优雅方式,在 .NET 生态中无处不在。从 Stream 的装饰器体系到 ASP.NET Core 的中间件管道,再到 DI 中的服务装饰,掌握装饰器模式有助于理解和设计灵活的系统架构。使用时应注意装饰器的组合顺序,并通过日志和监控保持对调用链的可观测性。
关键知识点
- 模式不是目标,降低耦合和控制变化才是目标。
- 先找变化点、稳定点和协作边界,再决定是否引入模式。
- 同一个模式在不同规模下的收益和代价差异很大。
- 框架与语言特性类主题要同时理解运行方式和工程组织方式。
项目落地视角
- 优先画出参与对象、依赖方向和调用链,再落到代码。
- 把模式放到一个真实场景里,比如支付、规则引擎、工作流或插件扩展。
- 配合单元测试或契约测试,保证重构后的行为没有漂移。
- 明确项目入口、配置管理、依赖管理、日志和测试策略。
常见误区
- 为了看起来“高级”而套模式。
- 把简单问题拆成过多抽象层,导致阅读和排障都变难。
- 只会背 UML,不会解释为什么这里需要这个模式。
- 把 notebook 或脚本风格直接带入长期维护项目。
进阶路线
- 继续关注模式之间的组合用法,而不是孤立记忆。
- 从业务建模、演进策略和团队协作角度看模式的适用性。
- 把模式结论沉淀为项目模板、基类或约束文档。
- 继续补齐部署、打包、监控和性能调优能力。
适用场景
- 当你准备把《装饰器模式详解》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在业务规则频繁变化、分支增多或对象协作复杂时引入。
- 当你希望提高扩展性,但又不想把系统拆得过度抽象时,这类主题很有参考价值。
落地建议
- 先识别变化点,再决定是否引入模式,而不是反过来套模板。
- 优先为模式的边界、依赖和调用路径画出简单结构图。
- 把模式落到一个明确场景,例如支付、规则计算、插件扩展或工作流。
排错清单
- 检查抽象层是否过多,导致调用路径和责任不清晰。
- 确认引入模式后是否真的减少了条件分支和重复代码。
- 警惕“为了模式而模式”,尤其是在简单业务里。
复盘问题
- 如果把《装饰器模式详解》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《装饰器模式详解》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《装饰器模式详解》最大的收益和代价分别是什么?
