C#装饰器模式:内部方法互调时装饰器失效如何解决?
解决装饰器内部方法调用不生效的问题
你的问题核心是:当MessageProcessor内部调用自身的ProcessMessage方法时,并没有经过装饰器的拦截逻辑——因为这种内部调用直接指向了类的原始方法,而非装饰后的接口实例。下面是几种可行的解决办法:
方法一:让实现类通过接口引用调用自身
修改MessageProcessor,将自身赋值给一个接口类型的字段,内部调用时使用这个字段而非直接调用方法。这样当装饰器包装实例后,这个字段指向的是装饰后的对象,调用就会经过装饰器逻辑。
public class MessageProcessor : IMessageProcessor { private readonly IMessageProcessor _self; // 构造函数中把自身赋值给接口字段 public MessageProcessor() { _self = this; } // 如果使用依赖注入框架,也可以通过构造注入获取自身的接口实例 // public MessageProcessor(IMessageProcessor self) // { // _self = self; // } public async Task ProcessMessages(){ // ... 其他逻辑 foreach (Message message in messages){ await _self.ProcessMessage(message); // 调用接口方法而非直接调用类方法 } // ... 其他逻辑 } public Task ProcessMessage(Message message){ // 消息处理逻辑 } }
方法二:拆分职责,分离批量与单消息处理逻辑
遵循单一职责原则,把单条消息的处理逻辑抽成独立服务,让MessageProcessor依赖这个服务而非调用自身方法。这样装饰器可以分别作用在批量处理和单消息处理的服务上,从根源避免内部调用的问题。
// 定义单消息处理接口 public interface IMessageHandler{ Task HandleMessage(Message message); } // 单消息处理实现 public class MessageHandler : IMessageHandler{ public Task HandleMessage(Message message){ // 具体消息处理逻辑 } } // 修改后的批量处理器 public class MessageProcessor : IMessageProcessor { private readonly IMessageHandler _messageHandler; public MessageProcessor(IMessageHandler messageHandler){ _messageHandler = messageHandler; } public async Task ProcessMessages(){ // ... 其他逻辑 foreach (Message message in messages){ await _messageHandler.HandleMessage(message); } // ... 其他逻辑 } // 保留原接口方法,内部转发给单消息处理器 public Task ProcessMessage(Message message){ return _messageHandler.HandleMessage(message); } } // 给单消息处理器添加错误处理装饰器 public class ErrorHandlerMessageHandlerDecorator : IMessageHandler{ private readonly IMessageHandler _decorated; public ErrorHandlerMessageHandlerDecorator(IMessageHandler decorated){ _decorated = decorated; } public async Task HandleMessage(Message message){ try{ await _decorated.HandleMessage(message); } catch{ // 重试、日志等错误处理逻辑 } } }
方法三:使用动态代理(第三方库)
如果项目允许引入第三方库(比如Castle DynamicProxy),可以通过动态代理实现类内部方法调用的拦截。动态代理会生成类的代理对象,所有方法调用(包括内部调用)都会经过代理逻辑,自然就能触发装饰器的拦截。不过这种方式相对复杂,适合已经在使用动态代理的场景。
内容的提问来源于stack exchange,提问作者tabs0n
相关产品推荐
相关产品推荐

