我正在重构相当大一部分意大利面条代码。简而言之,它是一个很大的“上帝之类”类,根据某些条件分为两个不同的过程。这两个过程都很冗长,并且有很多重复的代码。
所以我的第一个努力就是将这两个进程提取到他们自己的类中,并将公共代码放在他们都继承的父代中。
它看起来像这样:
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博客的狂热读者,在this entry他解释说这段代码有时间耦合气味,因为在数据提供程序处于可用状态之前必须调用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方法中进行数据库访问的对象不是一个好的设计。
是否有任何指导方针,最佳做法或某些事情表明这实际上是一个坏主意?
感谢您的帮助。
答案 0 :(得分: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.
}
}