C#:Strategy オブジェクトの Abstract Factory として機能する Abstract Strategy 基本クラス
-
18-09-2019 - |
質問
私は会社用に、基本的に地理的な入力を使用して表形式の結果を生成する Web ベースのツールを作成しようとしています。現在、3 つの異なるビジネス分野が私のツールを使用し、3 つの異なる種類の出力を受け取ります。幸いなことに、すべての出力はマスター テーブル - 子テーブルという同じ考え方に基づいており、共通のマスター テーブルも共有しています。
残念ながら、いずれの場合も、子テーブルの関連する行には、大きく異なるデータが含まれています。これが唯一の論点なので、 FetchChildData
メソッドを別のクラスに追加する DetailFinder
. 。結果として、私のコードは次のようになります。
DetailFinder DetailHandler;
if (ReportType == "Planning")
DetailHandler = new PlanningFinder();
else if (ReportType == "Operations")
DetailHandler = new OperationsFinder();
else if (ReportType == "Maintenance")
DetailHandler = new MaintenanceFinder();
DataTable ChildTable = DetailHandler.FetchChildData(Master);
PlanningFinder、OperationsFinder、MaintenanceFinder はすべて DetailFinder のサブクラスです。
別のビジネス分野のサポートを追加するよう依頼されたところですが、これを継続することは望ましくありません if
ブロックトレンド。私が好むのは、次のような解析メソッドがあることです。
DetailFinder DetailHandler = DetailFinder.Parse(ReportType);
しかし、どのようにすればよいのか迷っています DetailFinder
if ブロックを Parse
方法。サブクラスが自分自身を抽象クラスに登録する方法はありますか? DetailFinder
?
解決
あなたは生成に関する方法と種類のマップを使用する場合があります:
public class DetailFinder
{
private static Dictionary<string,Func<DetailFinder>> Creators;
static DetailFinder()
{
Creators = new Dictionary<string,Func<DetailFinder>>();
Creators.Add( "Planning", CreatePlanningFinder );
Creators.Add( "Operations", CreateOperationsFinder );
...
}
public static DetailFinder Create( string type )
{
return Creators[type].Invoke();
}
private static DetailFinder CreatePlanningFinder()
{
return new PlanningFinder();
}
private static DetailFinder CreateOperationsFinder()
{
return new OperationsFinder();
}
...
}
として使用されます:
DetailFinder detailHandler = DetailFinder.Create( ReportType );
私は、これはあなたのif文よりもはるかに優れているか分からないが、それはすることは自明簡単に読み取りおよび延長ありません。単に生成に関する方法とCreators
マップにエントリを追加します。
別の方法としては、常に単純にコンストラクタを呼び出すために予定されている場合はタイプにActivator.CreateInstanceを使用し、その後、レポートタイプとファインダー種類のマップを保存することです。オブジェクトの作成で、より複雑があった場合は、上記のファクトリメソッドの詳細は、おそらく、より適切であろう。
public class DetailFinder
{
private static Dictionary<string,Type> Creators;
static DetailFinder()
{
Creators = new Dictionary<string,Type>();
Creators.Add( "Planning", typeof(PlanningFinder) );
...
}
public static DetailFinder Create( string type )
{
Type t = Creators[type];
return Activator.CreateInstance(t) as DetailFinder;
}
}
他のヒント
IoC コンテナを使用することもできます。その多くでは、異なる名前またはポリシーを持つ複数のサービスを登録できます。
たとえば、仮想の IoC コンテナを使用すると、次のことができます。
IoC.Register<DetailHandler, PlanningFinder>("Planning");
IoC.Register<DetailHandler, OperationsFinder>("Operations");
...
その後:
DetailHandler handler = IoC.Resolve<DetailHandler>("Planning");
このテーマのいくつかのバリエーション。
次の IoC 実装を確認できます。
限り大きなif
ブロックまたはswitch
文とか、それが何であれだけ一箇所に表示され、それが保守のために悪いわけではないので、その理由のためにそれを心配しないでください。
しかし、物事は異なっています。あなたが本当に新しいDetailFindersが自分自身を登録することができるようにしたい場合は、マネージド拡張フレームワーク<を見てみたいことがあり/>基本的にあなたが「アドイン」フォルダまたは類似、およびコアアプリケーションに新しいアセンブリをドロップすることを可能にすることは、自動的に新しいDetailFindersをピックアップします。
しかし、私は、これはあなたが本当に必要とする拡張性の量であることはよく分からない。
if..else ブロックが増大し続けるのを避けるために、ブロックを切り替えて、個々のファインダーがどの型を処理するかをファクトリ クラスに登録することができます。
ファクトリ クラスは初期化時にすべての可能なファインダーを検出し、それらをハッシュマップ (辞書) に保存する必要があります。これは、Mark Seemann が示唆しているように、リフレクションやマネージ拡張性フレームワークを使用することによって行うことができます。
ただし、これが過度に複雑になることに注意してください。必要なときにリフレクタリングすることを目的として、現時点で機能する可能性のある最も単純なことを実行することを好みます。もう 1 つのファインダー タイプだけが必要な場合は、複雑な自己構成フレームワークを構築しないでください ;)
あなたは、リフレクションを使用することができます。 DetailFinderのParseメソッドのためのサンプルコードは(そのコードにエラーチェックを追加することを忘れないでください)があります:
public DetailFinder Parse(ReportType reportType)
{
string detailFinderClassName = GetDetailFinderClassNameByReportType(reportType);
return Activator.CreateInstance(Type.GetType(detailFinderClassName)) as DetailFinder;
}
メソッドGetDetailFinderClassNameByReportType
は、設定ファイルなどから、データベースからクラス名を取得することができます。
私は、「プラグイン」のパターンについての情報は、あなたの場合に有用であろうと思う:EAAの P:プラグイン>
のマークが言ったように、それはすべて一つの場所になりますので、大きな場合/スイッチブロックが悪いわけではないが、(コンピュータサイエンスのすべてが宇宙のいくつかの種類に類似性を得ることについて、基本的です)。
私はおそらく(これは私のための型システムを機能させる)多型を使用することになり、言いました。各レポートはFindDetailsメソッドを実装しているあなたは、とにかく細部探知機のいくつかの種類で終了するつもりだので、(私は彼らがレポート抽象クラスから継承する必要があると思います)。これはまた、関数型言語からパターンマッチングと代数データ型をシミュレートします。