5

私はスパゲッティコードのかなりの部分をリファクタリングしているところです。一言で言えば、それはある条件に応じて2つの異なるプロセスに分岐する大きな「神のような」クラスです。どちらのプロセスも時間がかかり、重複したコードがたくさんあります。

したがって、私の最初の取り組みは、これら2つのプロセスを独自のクラスに抽出し、両方が継承する親に共通のコードを配置することでした。

これは次のようになります。

public class ExportProcess
{
   public ExportClass(IExportDataProvider dataProvider, IExporterFactory exporterFactory)
   {
       _dataProvider = dataProvider;
       _exporterFactory = exporterFactory;
   }

   public void DoExport(SomeDataStructure someDataStructure)
   {
      _dataProvider.Load(someDataStructure.Id);

      var exporter = _exporterFactory.Create(_dataProvider, someDataStructure);

      exporter.Export();
   }
}

私はMarkSeemannのブログを熱心に読んでいます。このエントリでは、データプロバイダーが使用可能な状態になる前にLoadメソッドを呼び出す必要があるため、このコードには一時的な結合の匂いがあると説明しています。

それに基づいて、とにかくファクトリから返されたオブジェクトにオブジェクトが注入されているので、これを行うためにファクトリを変更することを考えています。

public IExporter Create(IExportDataProvider dataProvider, SomeDataStructure someDataStructure)
{
   dataProvider.Load(someDataStructure.Id);

   if(dataProvider.IsNewExport)
   {
      return new NewExportExporter(dataProvider, someDataStructure);
   }
   return new UpdateExportExporter(dataProvider, someDataStructure);
}

「DataProvider」という名前から、Loadメソッドが実際にデータベースアクセスを行っていると推測したかもしれません。

抽象ファクトリのcreateメソッド内でデータベースアクセスを行うオブジェクトは適切な設計ではないということを教えてくれます。

これが事実上悪い考えであると言うガイドライン、ベストプラクティス、または何かがありますか?

ご協力いただきありがとうございます。

4

1 に答える 1

2

通常、ファクトリは要求されたインターフェイスの具象型または抽象型を解決するために使用されるため、コンシューマーを実装から切り離すことができます。したがって、通常、ファクトリは具象型を検出または指定し、依存関係の解決を支援し、具象型をインスタンス化して返します。ただし、実行できることと実行できないことに関する厳格なルールや迅速なルールはありませんが、具象型を解決してインスタンス化するために必要なリソースのみに十分なアクセス権を付与することは賢明です。

ファクトリのもう 1 つの良い使い方は、コンシューマーに関係のない型の依存関係をコンシューマーから隠すことです。たとえば、IExportDataProvider内部的にのみ関連しているように見え、消費者から抽象化できます ( などExportProcess)。

ただし、あなたの例のコードの匂いの 1 つは、IExportDataProvider使用方法です。現在動作しているように見える方法では、一度インスタンスを取得しますが、その後の使用でその状態を変更することができます (を呼び出すことによりLoad)。これにより、同時実行性と破損状態の問題が発生する可能性があります。そのタイプが何をするのか、あなたの で実際にどのように使用されているのかわからないのでIExporter、推奨するのは難しい. 以下の例では、プロバイダーがステートレスであると想定できるように調整を行い、代わりLoadにファクトリがエクスポーターの具象型を解決するために使用できるある種の状態オブジェクトを返し、それにデータを提供できるようにします。必要に応じて調整できます。一方、プロバイダーがステートフルでなければならない場合は、IExportDataProviderFactory、それをエクスポータ ファクトリで使用し、エクスポータ ファクトリの への呼び出しごとにファクトリからプロバイダの新しいインスタンスを作成しますCreate

public interface IExporterFactory
{
    IExporter Create(SomeDataStructure someData);
}

public class MyConcreteExporterFactory : IExporterFactory
{
     public MyConcreteExporterFactory(IExportDataProvider provider) 
     {
          if (provider == null) throw new ArgumentNullException();

          Provider = provider;
     }

     public IExportDataProvider Provider { get; private set; }      

     public IExporter Create(SomeDataStructure someData)
     {
         var providerData = Provider.Load(someData.Id);

         // do whatever. for example...
         return providerData.IsNewExport ? new NewExportExporter(providerData, someData) : new UpdateExportExporter(providerData, someData);
     }
}

そして消費します:

public class ExportProcess
{
    public ExportProcess(IExporterFactory exporterFactory)
    {
        if (exporterFactory == null) throw new ArgumentNullException();

        _exporterFactory = factory;
    }

    private IExporterFactory _exporterFactory;

    public void DoExport(SomeDataStructure someData)
    {
        var exporter = _exporterFactory.Create(someData);
        // etc.
    }
}
于 2012-10-29T22:03:54.163 に答える