2012-02-02 58 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