我有一个服务,用于创建和分派执行命令。它可以将命令写入队列以进行延迟处理,也可以立即执行它们。
出于这些目的,我有类似的东西:
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<>
实施。事实上,我想排队的命令执行将面临同样的问题:我稍后会抓取这条消息,不得不以某种方式查找处理程序。
所以我能看到的选择是:
ICommandExecutor
用作依赖项,只需直接使用ICommandHandler<>
即可。但我想要通过标准类包装所有命令处理程序执行的选项 - 以一致的方式捕获异常,或以其他一致的方式管理执行。我真的很喜欢为立即/延迟执行提供一致的接口(调用Write或Execute)ICommandExecutor
并让它解析命令。这似乎打破了一个想法,即应该有一个用DI组成对象图的调用,并且可能“隐藏”依赖关系ICommandExecutor
的实现将所有ICommandHandler<>
作为要注入的依赖项 - 因此它可以手动选择它想要的那个。然而,这似乎也不理想,因为系统上的所有处理程序都将在那时实例化是否有第四种选择,或者我是否需要咬住其中一种妥协?
答案 0 :(得分:3)
对我而言,使用ICommandQueueWriter
和ICommandExecutor
似乎很奇怪。为什么消费者(在您的情况下为MyService
)必须知道一个命令是排队的,而另一个命令是直接执行的。我认为这应该是透明的。
是否有ICommandExecutor的实现 ICommandHandler&lt;&gt; s作为要注入的依赖项
这将导致严重的维护问题,因为您将定期添加新的命令处理程序,这将导致您每次都必须更新命令执行程序的构造函数。
虽然您也可以注入一组命令处理程序,但是每次要执行一个命令处理程序时,这仍然会强制您迭代列表以获得正确的实现。这将随着时间的推移变慢,因为您将定期添加新的命令处理程序。
将DI容器或root传递给ICommandExecutor并让它解决 命令。这似乎打破了应该有一个电话的想法 使用DI撰写对象图,并可能“隐藏”依赖关系
如果您执行此操作,似乎您正在应用Service Locator anti-pattern,但只有ICommandExecutor
是应用程序代码的一部分时才会出现这种情况。诀窍是制作Composition Root的ICommandExecutor
部分。这个solves the problem因为组合根已经非常紧密地耦合到你的容器了。
答案 1 :(得分:0)
让ICommandExecutor的实现将所有ICommandHandler&lt;&gt; s作为要注入的依赖项 - 因此它可以手动选择它想要的那个。然而,这似乎并不理想,因为系统上的所有处理程序都将在那时被实例化
如果你想注入处理程序,但不希望在注入时实例化处理程序,那么
喜欢,
IEnumerable(of Lazy(of ICommandHandler, ICommandHandlerMetadata))
其中ICommandExecutorMetadata
包含与命令处理程序
public interface ICommandHandlerMetadata
readonly property CommandType as Type
end interface
在命令执行程序中,将集合转换为对(Type, Lazy(of ICommandHandler, ICommandHandlerMetadata))
对的字典,其中Type
是通过Lazy.Metadata.CommandType
从元数据获取的命令类型。
在CommandExecutor.Execute
方法中,lazy启动的命令处理程序是通过命令类型从字典中获取的。这个操作非常快。实际CommandHandler
是通过Lazy.Value
获得的。
实际CommandHandler
仅实例化一次,并且恰好在执行相应命令时。