在注入之前在工厂内调用类的初始化方法是一个很好的设计选项

Ser*_*ero 5 c# refactoring factory-pattern

我正在重构相当大部分的意大利面条代码.简而言之,它是一个很大的"上帝之类"类,根据某些条件分为两个不同的过程.这两个过程都很冗长,并且有很多重复的代码.

所以我的第一个努力就是将这两个进程提取到他们自己的类中,并将公共代码放在他们都继承的父代中.

它看起来像这样:

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

我是Mark Seemann博客的狂热读者,在这篇文章中他解释说这段代码具有时间耦合气味,因为在数据提供程序处于可用状态之前必须调用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);
}
Run Code Online (Sandbox Code Playgroud)

由于名称"DataProvider",您可能猜到Load方法实际上正在进行数据库访问.

有些东西告诉我在抽象工厂的create方法中进行数据库访问的对象不是一个好的设计.

是否有任何指导方针,最佳做法或某些事情表明这实际上是一个坏主意?

谢谢你的帮助.

Hac*_*ese 2

通常,工厂用于解析所请求接口的具体类型或抽象类型,因此您可以将使用者与实现分离。因此,工厂通常只会发现或指定具体类型,帮助解决依赖关系,并实例化具体类型并返回它。然而,对于它能做什么或不能做什么并没有硬性或快速的规则,但明智的做法是只给予它足够的访问权限来解析和实例化具体类型所需的资源。

工厂的另一个很好的用途是向消费者隐藏与消费者不相关的类型依赖项。例如,它似乎IExportDataProvider只在内部相关,并且可以从消费者中抽象出来(例如ExportProcess)。

然而,您的示例中的一种代码味道是如何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);
     }
}
Run Code Online (Sandbox Code Playgroud)

然后消费:

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.
    }
}
Run Code Online (Sandbox Code Playgroud)