如何将依赖项注入应用于抽象工厂
本文关键字:应用于 抽象 工厂 注入 依赖 | 更新日期: 2023-09-27 18:22:21
我刚刚完成了Mark Seemann的《.NET依赖注入》一书,现在我正在尝试重构一些遗留代码。(在这个阶段,我不依赖任何特定的DI容器,而只是试图将所有的依赖关系转移到一个地方)。
我看到的是下面的工厂类,它通过用archiveReader.GetArchiveType()
读取存档的前几个字节来确定ArchiveType
,然后返回基于ArchiveType
枚举的ArchiveRestorer
的实例。
public class ArchiveRestorerFactory : IArchiveRestorerFactory
{
public ArchiveRestorer Create(ArchiveReader archiveReader)
{
ArchiveType type = archiveReader.GetArchiveType();
switch (type)
{
case ArchiveType.CurrentData:
return new CurrentDataArchiveRestorer(archiveReader);
break;
case ArchiveType.HistoricalData:
return new HistoricalDataArchiveRestorer(archiveReader);
break;
case ArchiveType.AuditTrail:
return new AuditTrailArchiveRestorer(archiveReader);
break;
default:
throw new Exception("ArchiveRestorerFactory error: Unknown value for ArchiveType.");
}
}
}
如何重构它,使类不依赖于具体类型CurrentDataArchiveRestorer
、HistoricalDataArchiveRestorer
和AuditTrailArchiveRestorer
?
我应该把三个混凝土修复师调到工厂的建造师那里吗?
public ArchiveRestorer Create(ArchiveReader archiveReader,
ArchiveRestorer currentDataArchiveRestorer,
ArchiveRestorer historicalDataArchiveRestorer,
ArchiveRestorer auditTrailDataArchiveRestorer)
{
// guard clauses...
// assign to readonly fields
}
这似乎是这里建议的方法,但当只需要一个恢复程序时,它会实例化所有三个恢复程序吗?如果我有20种不同的可能的具体实现呢?
我觉得我应该为每种类型的修复程序实现一个混凝土工厂,然后返回,但我只会用另一个new
替换一个。
重构它的最佳方法是什么?
考虑到您已经获得的代码,我这样做的方法是为这些对象中的每一个创建一个具有Create()
方法的工厂。
我也会为这些工厂提供一个接口,并让它们继承通用工厂接口。
然后,您可以使用这些接口作为注入构造函数的点。
这将被称为类似于此:
case ArchiveType.CurrentData:
return _currentDateArchiveRestorerFactory.Create(archiveReader);
break;
或者,最好有一个单独的工厂来创建给定类型的实例。由于所有这些对象都是恢复器,您可以仅基于enum
而不是switch
创建实例。
_restorerFactory.Create(ArchiveType.CurrentData);
为什么不让ArchiveReader负责创建适当的ArchiveRestorer?然后代码的第一次迭代看起来是这样的:
public class ArchiveRestorerFactory : IArchiveRestorerFactory
{
public ArchiveRestorer Create(ArchiveReader archiveReader)
{
ArchiveRestorer restorer = archiveReader.GetArchiveRestorer();
return restorer;
}
}
到那时,很明显工厂是多余的,所以在代码的第二次迭代中,你可以把它扔掉,让消费者直接调用ArchiveReader。
用一个方法创建一个接口,并让三个archiver类实现该接口,然后在create方法中,参数类型将只是接口,它将通过调用您刚刚创建的接口的方法来返回所需的对象。所以在create方法中不需要具体的类型。
interface ILogger
{
void Log(string data);
}
class Logger : ILogger
{
.
.
.
}
此时,您使用一个中间工厂对象来返回要在组件中使用的记录器:
class MyComponent
{
void DoSomeWork()
{
// Get an instance of the logger
ILogger logger = Helpers.GetLogger();
// Get data to log
string data = GetData();
// Log
logger.Log(data);
}
}
class Helpers
{
public static ILogger GetLogger()
{
// Here, use any sophisticated logic you like
// to determine the right logger to instantiate.
ILogger logger = null;
if (UseDatabaseLogger)
{
logger = new DatabaseLogger();
}
else
{
logger = new FileLogger();
}
return logger;
}
}
class FileLogger : ILogger
{
.
.
.
}
class DatabaseLogger : ILogger
{
.
.
.
}
我将通过商定命名约定并利用Unity命名注册的能力来解决这个问题。此处的示例:https://dannyvanderkraan.wordpress.com/2015/06/29/real-world-example-of-dependency-injection-based-on-run-time-values/