我'm currently implementing the Command-Handler Pattern for a service I'm设计Command本质上是Handler的 .Handle()
方法的DTO . 当我开始实现各种具体课程时,我意识到为了满足开放/封闭原则和单一责任原则,我可能最终会有数千个Command和Handler类,这将严重违反不要重复自己的原则 .
例如,我封装的部分进程需要从60个奇数表中删除 ProjectId
的所有数据以重置它们 . 如果我将每个实现为一个原子混凝土Command对象和具体的CommandHandler对象,那么我将只有第一步的120个类 . 他们都将完全遵循SRP和OCP,但DRY严重受挫......
public class DeleteProjectLogCommand : CommandBase
{
public long? ProjectId { get; set; }
}
public class DeleteProjectLogCommandHandler : ICommandHandler<DeleteProjectLogCommand>
{
public async Task<Feedback<DeleteProjectLogCommand>> Handle(DeleteProjectLogCommand command, CancellationToken token)
{
// ...
}
}
或者,我可以实现一个单一的,多用途的命令和处理程序类,并且可以使用 ProjectTables
枚举来代替所有离散类 .
public class DeleteTableByProjectIdCommand : CommandBase
{
public DeleteTableByProjectIdCommand(ProjectTables table, long? projectId) {}
public long? ProjectId { get; set; }
public ProjectTables Table { get; set; }
}
public class DeleteTableByProjectIdCommandHandler : ICommandHandler<DeleteTableByProjectIdCommand>
{
public async Task<Feedback<DeleteTableByProjectIdCommand>> Handle(DeleteTableByProjectIdCommand command, CancellationToken token)
{
switch(command.Table)
{
case ProjectTables.ProjectLog:
// x60 tables
break;
}
}
}
但是这会违反开放/封闭原则,因为如果添加新表,枚举和使用它的每个地方都必须更新 . 更不用说从60箱开关声明中得到的气味 .
Sooo ......谁赢了?干或SRP和OCP?
3 回答
不要太过于与首字母缩略词捆绑在一起 . 专注于编写感觉正确的代码 . 原子命令是一个非常好的主意,但是你需要适当的粒度级别我通常认为命令是一个完整的(用户)操作 .
你的enum和God开关的设计没有通过基本的健全性测试,如果不修改类本身就无法扩展,所以它一定是坏的,对吧?
考虑使用RelayCommand:http://msdn.microsoft.com/en-us/magazine/dn237302.aspx
这是一个实现ICommand的命令,但是希望为实际工作注入一个委托 . 许多MVVM框架包含一个开箱即用的RelayCommand(或DelegateCommand) .
因此,您实现了命令接口,并要求在ctor中注入Action或Action <T> . 执行会触发操作 . 如果您需要将某些内容传递给操作,则可以使用“ofT”版本,或将其包含在您传递的代理中 .
这允许您:
具有Command的单个实现(或2,如果您支持泛型)
将实际命令逻辑放在其他位置(例如在域对象中)
如果有意义,命令逻辑实际上可以是您的域类的私有成员,由于您传递了委托,因此该命令会公开该成员 .
例:
数百个命令和处理程序都不会违反DRY,因为命令包含特定用例的命令,即每个命令和处理程序都在实现业务用例 . 你有相同的商业用例吗?
例如,我在一个具有不同资源类型的应用程序上工作 . 但我只有一个看起来像这样的DeleteCommand
当然这不是全部,因为整个应用程序被设计为使用N种资源类型,这意味着我的实体存储主要是一个不关心实体结构的键值存储 . 删除只需要一个id .
删除资源后,将发布事件,该事件由读取模型更新程序处理,然后删除该资源类型的查询数据 .
添加新资源时,我不单独工作,他们正在使用其他组件来实现DRY和OCP . 而OCP有点fuzzy principle .