2012-02-02 93 views
5

我刚刚完成了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."); 
     } 
    } 
} 

如何重构这使得类不依赖于具体的类型CurrentDataArchiveRestorerHistoricalDataArchiveRestorerAuditTrailArchiveRestorer

我应该将三个混凝土恢复器移到工厂的构造器中吗?

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

这似乎是提出这一方法here,但随后将实例所有三个修复者时,只需要一个?如果我有20种不同的可能的具体实现呢?

我觉得我应该为每种类型的恢复器实施一个混凝土工厂,然后将其替换为一个new

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

+1

我认为你的情况可能更适合于责任链模式。查看[此示例](http://davidhayden.com/blog/dave/archive/2008/11/19/ChainResponsibilityDesignPatternUnityDependencyInjectionContainer.aspx),了解与特定DI容器(Unity)结合使用模式的情况。 – 2012-02-02 18:19:31

+0

我不会以这种方式实现链的注册和组装,但它绝对是一种有效的方法。 – 2012-02-02 18:52:37

+0

除了(多余的)使用'enum',你目前的实现有什么特别的麻烦? – 2012-02-02 21:09:09

回答

2

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

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

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

这将是所谓的与此类似:

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

或者,它可能是最好有一个工厂,创建一个给定类型的实例。由于所有这些对象都是恢复器,因此您可以根据enum而不是switch创建实例。

_restorerFactory.Create(ArchiveType.CurrentData); 
1

让一个接口的一种方法与收益类型的接口,并且让一家归档类实现该接口,然后在创建方法的参数类型将只是界面,它会通过调用返回所需的对象刚刚创建的接口的方法。所以你不需要创建方法中的具体类型。

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

为什么不把ArchiveReader负责创建适当的ArchiveRestorer?然后代码的第一次迭代是这样的:

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

届时,它应该是很明显的,工厂是多余的,所以在代码的第二次迭代,你可以扔掉它让消费者调用直接存档阅读器。

+0

这听起来像一个很好的重构,我会实现它,但它不解决依赖问题。它将依赖关系移动到'ArchiveReader'类。 'ArchiveReader.GetArchiveRestorer()'仍然需要''新''正确的子类'ArchiveRestorer'。 – shamp00 2012-02-03 10:18:13

+1

将它们注入到ArchiveReader中。这一切归结为它如何选择适当的子类型。既然你没有分享,那有点难以回答。但是,如果你决定分享这个,那么它应该是一个新的问题。 – 2012-02-03 10:49:57

+0

这是否既不能解决依赖性问题(如上面的评论中提到的),也打破了SRP?我正在阅读你的书,但是在试图将理论应用于真实代码时,我正在赶上这些东西。 – Steve 2015-09-16 15:24:57