5

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

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

它看起来像这样:

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();
   }
}

我是 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);
}

由于名称“DataProvider”,您可能猜到 Load 方法实际上是在进行数据库访问。

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

是否有任何指导方针、最佳实践或说这实际上是个坏主意?

谢谢你的帮助。

4

1 回答 1

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);
     }
}

然后消费:

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 回答