3

私は、本質的に地理的な入力を使用して表形式の結果を生成する、会社用の 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ますか?

4

6 に答える 6

3

IoC コンテナーを使用できます。それらの多くでは、複数のサービスを異なる名前またはポリシーで登録できます。

たとえば、仮想の IoC コンテナーを使用すると、次のことができます。

IoC.Register<DetailHandler, PlanningFinder>("Planning");
IoC.Register<DetailHandler, OperationsFinder>("Operations");
...

その後:

DetailHandler handler = IoC.Resolve<DetailHandler>("Planning");

このテーマのいくつかのバリエーション。

次の IoC 実装を確認できます。

于 2010-01-04T23:17:18.387 に答える
1

タイプのマップを作成メソッドに使用したい場合があります。

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マップに追加するだけです。

もう 1 つの方法は、レポート タイプとファインダ タイプのマップを保存し、常に単にコンストラクタを呼び出す場合は、そのタイプで 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;
      }
}
于 2010-01-04T22:38:41.383 に答える
0

反射を使用できます。DetailFinderのParseメソッドのサンプルコードがあります(そのコードにエラーチェックを追加することを忘れないでください)。

public DetailFinder Parse(ReportType reportType)
{
    string detailFinderClassName = GetDetailFinderClassNameByReportType(reportType);
    return Activator.CreateInstance(Type.GetType(detailFinderClassName)) as DetailFinder;
}

メソッドGetDetailFinderClassNameByReportTypeは、データベース、構成ファイルなどからクラス名を取得できます。

「プラグイン」パターンに関する情報は、あなたの場合に役立つと思います:EAAのP:プラグイン

于 2010-01-04T22:44:47.697 に答える
0

大きなifブロックやswitchステートメント、またはそれが何であれ、1 か所にしか表示されない限り、保守性は悪くありません。そのため、心配する必要はありません。

ただし、拡張性に関しては事情が異なります。新しい DetailFinders を自分自身で登録できるようにしたい場合は、基本的に新しいアセンブリを「アドイン」フォルダーなどにドロップできるようにするManaged Extensibility Frameworkを参照してください。コア アプリケーションはその後、新しい DetailFinders を自動的に取得します。

ただし、これが本当に必要な拡張性の量かどうかはわかりません。

于 2010-01-04T22:33:44.330 に答える
0

if..else ブロックが増え続けるのを避けるために、それを切り替えて、個々のファインダーがファクトリ クラスで処理する型を登録することができます。

初期化時のファクトリ クラスは、すべての可能なファインダーを検出し、それらをハッシュマップ (辞書) に格納する必要があります。これは、Mark Seemann が示唆するように、リフレクションおよび/または管理された拡張性フレームワークを使用することによって行うことができます。

ただし、これを過度に複雑にしないように注意してください。必要に応じてリフェクタを作成できるように、現在機能する可能性のある最も単純なことを行うことをお勧めします。もう 1 つのファインダー タイプしか必要ない場合は、複雑な自己構成フレームワークを作成しないでください ;)

于 2010-01-04T22:40:02.447 に答える