私は現在、コマンドが基本的にハンドラーの.Handle()
メソッドの DTO である、設計中のサービスのコマンドハンドラーパターンを実装しています。さまざまな具象クラスの実装を開始すると、Open/Closed Principle および Single Responsibility Principle を満たすために、何千もの Command および Handler クラスになる可能性があることに気付きます。これは、Don't Repeat Yourself Principle に大きく違反します。
たとえば、私がカプセル化しているプロセスの一部では、ProjectId
60 個の奇数テーブルからすべてのデータを削除してそれらをリセットする必要があります。それぞれをアトミックな具象 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;
}
}
}
ただし、新しいテーブルが追加された場合、列挙とそれを使用するすべての場所の両方も更新する必要があるため、これは Open/Closed Principle に違反します。60 ケースの switch ステートメントから得られる臭いは言うまでもありません。
すっごい…どっちが勝つ?DRY または SRP & OCP?