仕様パターン、FUNC 述語、およびパイプとフィルターの比較
質問
私はいくつかのR&Dの仕事をしているので、デザインパターンを探求しています。私は最近仕様パターンを読んでいて、参照されました これ 素晴らしい記事。
私はコードのシンプルさと清潔さに興味をそそられましたが、他のテクニックを使用して同じ清潔さを実装することと比較していくつかの比較を描き始めました。
サービスレイヤーの次のインターフェイス契約を検討してください。
public interface IFooDataService
{
ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification);
ICollection<Foo> GetFooByPredicate(Func<Foo,bool> predicate);
ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs);
}
だから、いくつかの最初のポイント:
- 3つすべてがFooオブジェクトのコレクションを返します
- 3つすべてが1つの1つの引数を取ります
- 仕様方法は、特定の要件へのアクセスを制限します
- 述語方法には基本的に制限はありません
- 検索ARGSメソッドは、特定の要件へのアクセスを制限します
さて、実装について:
public ICollection<Foo> GetFoosBySpecification(Specification<Foo> specification)
{
return fooDataRepository
.Find()
.Where(f => specification.IsSatisfiedBy(f))
.ToList();
}
public ICollection<Foo> GetFooByPredicate(Func<Foo, bool> predicate)
{
return fooDataRepository
.Find()
.Where(predicate)
.ToList();
}
public ICollection<Foo> GetFooBySearchArgs(FooSearchArgs searchArgs)
{
return fooDataRepository
.Find()
.WhereMeetsSearchCriteria(searchArgs)
.ToList();
}
実装に関するポイント:
- 3つすべてが実装が非常に簡単です(チェーンコードの1行)
- 外部から実装された仕様と検索のargsフィルタリング。
- 検索argsメソッドは単にienumerable拡張法を使用してargsを検査します
そうは言っても、3つのテクニックのいずれかをどのような条件で使用しますか?
仕様パターンに関する私の考え:
- それがビジネス/ドメインの要件を再利用可能なコンポーネントに分離するという点で素晴らしい
- 読みやすく、コードに英語を話させます
- かなりのコードが関係しています(インターフェイス、抽象クラス)。これを使用した場合、抽象化を共通のアセンブリに入れます(したがって、ソリューションに静的ファイルの束がありません)。
- サービスレイヤーではなく、仕様のみを変更することで要件を簡単に変更できます。
- ドメインロジック(仕様)の最高のテスト可能性
拡張方法(パイプとフィルター)に関する私の考え:
- ロジックでは「重い」が、それでも同じシンプルさをもたらします。
- サービスレイヤーから静的メソッドにロジックをクエリします
- それでも、ある種の「反射」が必要です(提供された検索argsを検査し、クエリを構築する)
- 特定のビジネス要件(特定のシナリオで便利な)を考えずに、最初にアーキテクチャ(リポジトリ、サービスレイヤー)をコーディングできます。
述語方法に関する私の考え:
- クエリを粗く粒状制御が必要な場合に使用できます。
- 仕様が無理になっている可能性のある小さなプロジェクトに適しています
私の最後の考えロジックは、ビジネス要件が前もって知られているが時間とともに変化する可能性のある複雑なビジネスアプリケーションに取り組んでいる場合、仕様パターンを使用することです。
しかし、「スタートアップ」であるアプリケーションの場合、IE要件は時間の経過とともに進化し、複雑な検証なしでデータを取得するための多数の方法があります。パイプとフィルターの方法を使用します。
あなたの考えは何ですか?上記の方法のいずれかで問題に遭遇した人はいますか?推奨事項はありますか?
これらのタイプの考慮事項が重要になるように、新しいプロジェクトを開始しようとしています。
助けてくれてありがとう。
仕様パターンの明確化のために編集します
仕様パターンの同じ使用法です。
Specification<Foo> someSpec; // Specification is an abstract class, implementing ISpecification<TEntity> members (And, Or, Not, IsSatisfiedBy).
someSpec = new AllFoosMustHaveABarSpecification(); // Simple class which inherits from Specification<Foo> class, overriding abstract method "IsSatisfiedBy" - which provides the actual business logic.
ICollection<Foo> foos = fooDataService.GetFoosBySpecification(someSpec);
解決
私の小さな経験から:
- ユーザーの要件は常に変更され、上司が常にその変更を許可する理由がわかりません。したがって、仕様に+1
- ここのプログラマーは、「ナレッジワーカー」というよりも「マニュアルワーカー」に似ています。あなたは知っています..一日中タイプする人。仕様を使用することで、全員が「タイプ」を確実にすることができます。これは私のプロジェクトの性質によってサポートされています。同じ目的のために多くの異なる実装が必要です。理由を聞かないでください。
- 最高のモジュール性と柔軟性、そしてもちろんテスト能力を提供するデザインパターンを使用します。これがちょっとした話です。
ある晴れた日に、私の仲間は、Xを計算する32種類の方法を計算できるクラスを書いたと私に言った。彼はすでにすべてを実装した。 Hoho、それはとても英雄的なプログラミングだったと思います。彼は夜中にそれを数週間費やしました。彼は自分が優れたプログラマーであると信じていたので、彼は彼の傑作を使うようにみんなに主張しました。
当時、私たちはユニットテストを気にしていなかったので、彼の傑作を使用しました。その時はどうなりましたか?コードは常にクラッシュしました。まあ、その時から、私は単体テストとモジュール性がどれほど重要かを認識しました。
他のヒント
まあ、最初に述語方法を書きます。たとえそれが他の2つのプライベート実装の詳細としてのみ使用されていても:
private ICollection<Foo> GetFoosBySpecification(Specification<Foo> spec)
{
return GetFooByPredicate(f => spec.IsSatisfiedBy(f));
}
検索引数関数は、同様のワンライナーです。
それを超えて、私は本当に要約で何も言うことができません。データ構造についてもっと知り、それらを探すための最良の方法を決定する必要があります。