ast*_*tef 2 c# oop dependency-injection
我有一个Command依赖于ICommandLogger接口的抽象基类:
public abstract class Command
{
public Command(ICommandLogger cmdLogger) { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)
现在所有的继承人看起来像:
public class ConcreteCommand : Command
{
public ConcreteCommand(CommandLoggersNamespace.ICommandLogger cmdLogger)
: base(cmdLogger)
{
}
}
Run Code Online (Sandbox Code Playgroud)
我不喜欢他们被迫知道ICommandLogger(他们不使用).
怎么解决这个问题?或者完全重新设计的原因是什么?
您的设计中存在一个问题,导致您遇到这些麻烦.首先,伐木是一个贯穿各领域的问题,你应该防止污染类.其次,如果让基类实现日志记录,那么将在logger基类上添加的下一个横切关注点是什么.第二个你开始添加另一个横切关注点,基类将违反单一责任原则.你的基类最终将成长为一个具有大量依赖关系的大型无法管理的类,并且有许多改变的理由.
相反,尝试添加日志作为装饰器.然而,你的设计会妨碍你有效地做到这一点,因为你可能会有几十个具体的命令,他们都需要自己的装饰器.但您设计的核心问题是混合数据和行为.让命令只是一个包含一些数据(DTO)的类,并将命令逻辑添加到它自己的类中; 让我们称之为命令处理程序.
最重要的是,让命令处理程序实现此接口:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
Run Code Online (Sandbox Code Playgroud)
这将是这样的:
public class MoveCustomerCommand
{
public Guid CustomerId;
public Address NewAddress;
}
public class MoveCustomerCommmandHandler : ICommandHandler<MoveCustomerCommand>
{
public void Handle(MoveCustomerCommand command)
{
// behavior here.
}
}
Run Code Online (Sandbox Code Playgroud)
这个设计的有趣之处在于,由于所有业务逻辑现在都隐藏在一个狭窄的接口后面,并且这个接口是通用的,因此通过使用装饰器包装处理程序来扩展系统的行为变得非常容易.例如一个日志装饰器:
public class LoggingCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
public LoggingCommandHandlerDecorator(
ICommandHandler<TCommand> decoratee, ILog log)
{
this.decoratee = decoratee;
this.log = log;
}
public void Handle(TCommand command)
{
this.log.Log("Executing " + typeof(TCommand).Name + ": " +
JsonConvert.Serialize(command));
try
{
this.decoratee.Handle(command);
}
catch (Exception ex)
{
this.log.Log(ex);
throw;
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于这LoggingCommandHandlerDecorator<TCommand>是通用的,它可以缠绕任何ICommandHandler<TCommand>.这允许您让消费者依赖某些ICommandHandler<TCommand>(例如ICommandHandler<MoveCustomerCommand>),并且可以在不更改单行代码的情况下向所有业务逻辑添加横切关注点.
在大多数情况下,这将完全消除使用基类的需要.
您可以在此处阅读有关此类设计的更多信息.