DI与命令模式

时间:2015-03-26 08:36:40

标签: c# dependency-injection

我有一个服务,用于创建和分派执行命令。它可以将命令写入队列以进行延迟处理,也可以立即执行它们。

出于这些目的,我有类似的东西:

class MyService
{
    private ICommandQueueWriter _commandQueueWriter;
    private ICommandExecutor _commandExecutor;

    public MyService(ICommandQueueWriter cqw, ICommandExecutor ce)
    {
        _commandQueueWriter = cqr;
        _commandExecutor = ce;
    }

    public void DoSomething()
    {
        _commandQueueWriter.Write(new SomeCommand());
        _commandExecutor.Execute(new SomeOtherCommand());
    }
}

该服务将处理各种命令。我有一系列ICommandHandler<>实现,将在DI容器中注册。

我的计划目前存在缺陷:ICommandExecutor实施需要访问所有ICommandHandler<>实施。事实上,我想排队的命令执行将面临同样的问题:我稍后会抓取这条消息,不得不以某种方式查找处理程序。

所以我能看到的选择是:

  1. 不要将ICommandExecutor用作依赖项,只需直接使用ICommandHandler<>即可。但我想要通过标准类包装所有命令处理程序执行的选项 - 以一致的方式捕获异常,或以其他一致的方式管理执行。我真的很喜欢为立即/延迟执行提供一致的接口(调用Write或Execute)
  2. 将DI容器或root传递给ICommandExecutor并让它解析命令。这似乎打破了一个想法,即应该有一个用DI组成对象图的调用,并且可能“隐藏”依赖关系
  3. ICommandExecutor的实现将所有ICommandHandler<>作为要注入的依赖项 - 因此它可以手动选择它想要的那个。然而,这似乎也不理想,因为系统上的所有处理程序都将在那时实例化
  4. 是否有第四种选择,或者我是否需要咬住其中一种妥协?

2 个答案:

答案 0 :(得分:3)

对我而言,使用ICommandQueueWriterICommandExecutor似乎很奇怪。为什么消费者(在您的情况下为MyService)必须知道一个命令是排队的,而另一个命令是直接执行的。我认为这应该是透明的。

  

是否有ICommandExecutor的实现   ICommandHandler&lt;&gt; s作为要注入的依赖项

这将导致严重的维护问题,因为您将定期添加新的命令处理程序,这将导致您每次都必须更新命令执行程序的构造函数。

虽然您也可以注入一组命令处理程序,但是每次要执行一个命令处理程序时,这仍然会强制您迭代列表以获得正确的实现。这将随着时间的推移变慢,因为您将定期添加新的命令处理程序。

  

将DI容器或root传递给ICommandExecutor并让它解决   命令。这似乎打破了应该有一个电话的想法   使用DI撰写对象图,并可能“隐藏”依赖关系

如果您执行此操作,似乎您正在应用Service Locator anti-pattern,但只有ICommandExecutor是应用程序代码的一部分时才会出现这种情况。诀窍是制作Composition RootICommandExecutor部分。这个solves the problem因为组合根已经非常紧密地耦合到你的容器了。

答案 1 :(得分:0)

  

让ICommandExecutor的实现将所有ICommandHandler&lt;&gt; s作为要注入的依赖项 - 因此它可以手动选择它想要的那个。然而,这似乎并不理想,因为系统上的所有处理程序都将在那时被实例化

如果你想注入处理程序,但不希望在注入时实例化处理程序,那么

  1. 向命令执行程序注入一组惰性实例化处理程序,其中包含包含命令类型的元数据。
  2. 喜欢,

    IEnumerable(of Lazy(of ICommandHandler, ICommandHandlerMetadata))
    

    其中ICommandExecutorMetadata包含与命令处理程序

    对应的命令类型
    public interface ICommandHandlerMetadata
        readonly property CommandType as Type
    end interface
    
    1. 在命令执行程序中,将集合转换为对(Type, Lazy(of ICommandHandler, ICommandHandlerMetadata))对的字典,其中Type是通过Lazy.Metadata.CommandType从元数据获取的命令类型。

    2. CommandExecutor.Execute方法中,lazy启动的命令处理程序是通过命令类型从字典中获取的。这个操作非常快。实际CommandHandler是通过Lazy.Value获得的。

    3. 实际CommandHandler仅实例化一次,并且恰好在执行相应命令时。