如何将依赖注入应用于抽象工厂

时间:2012-02-02 17:57:54

标签: c# dependency-injection

我刚刚完成了Mark Seemann的书 .NET中的依赖注入,现在我正在尝试重构一些遗留代码。 (在此阶段,我不依赖于任何特定的DI容器,而只是尝试将所有依赖项移动到一个位置。)

我正在查看以下工厂类,它通过使用ArchiveType读取存档的前几个字节来确定archiveReader.GetArchiveType(),然后基于此返回ArchiveRestorer的实例ArchiveType枚举。

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

如何重构此类以使该类不依赖于具体类型CurrentDataArchiveRestorerHistoricalDataArchiveRestorerAuditTrailArchiveRestorer

我应该将三个具体的恢复器移动到工厂的构造函数中吗?

public ArchiveRestorer Create(ArchiveReader archiveReader, 
    ArchiveRestorer currentDataArchiveRestorer, 
    ArchiveRestorer historicalDataArchiveRestorer, 
    ArchiveRestorer auditTrailDataArchiveRestorer)
{
    // guard clauses...
    // assign to readonly fields
}

这似乎是建议here的方法,但是当只需要一个时,它会实例化所有三个恢复器?如果我有20种不同的具体实现呢?

我觉得我应该为每种类型的恢复器实现一个具体的工厂并返回它,但之后我只是将一个new替换为另一个。{/ p>

重构这个的最佳方法是什么?

5 个答案:

答案 0 :(得分:2)

考虑到你已经获得的代码,我这样做的方法是为每个具有Create()方法的对象创建一个工厂。

我也有这些工厂的接口,并让它们从一般的工厂接口继承。

然后,您可以使用接口作为注入构造函数的点。

这将被称为类似于此:

case ArchiveType.CurrentData:
                return _currentDateArchiveRestorerFactory.Create(archiveReader);
                break;

或者,拥有一个创建给定类型实例的工厂可能会更好。由于所有这些对象都是还原器,因此您只需基于enum而不是switch创建实例。

_restorerFactory.Create(ArchiveType.CurrentData);

答案 1 :(得分:2)

为什么不让ArchiveReader负责创建适当的ArchiveRestorer?然后代码的第一次迭代看起来像这样:

public class ArchiveRestorerFactory : IArchiveRestorerFactory
{
    public ArchiveRestorer Create(ArchiveReader archiveReader)
    {
        ArchiveRestorer restorer = archiveReader.GetArchiveRestorer();
        return restorer;
    }
}

到那时,应该很明显工厂是多余的,所以在代码的第二次迭代中你可以抛弃它让消费者直接调用ArchiveReader。

答案 2 :(得分:1)

使用一个返回类型为该接口的方法创建一个接口,让三个归档类实现该接口,然后在create方法中,参数类型只是接口,它将通过调用接口方法返回所需的对象你刚刚创建。所以你在create方法中不需要具体的类型。

答案 3 :(得分:0)

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
{
    .
    .
    .
}

答案 4 :(得分:0)

我会通过同意命名约定并利用Unity的命名注册能力来解决这个问题。此示例如下:https://dannyvanderkraan.wordpress.com/2015/06/29/real-world-example-of-dependency-injection-based-on-run-time-values/