2012-10-29 31 views
5

我在重構的麪條代碼相當大一部分的過程之前調用一個工廠內的類初始化方法。簡而言之,它是一個巨大的「上帝」類,根據某種條件分爲兩個不同的過程。這兩個過程都很長,並且有很多重複的代碼。這是一個好的設計方案將其注入

所以我的第一次努力已經給這兩個過程提取到自己的類,並把通用代碼中他們都繼承父。

它看起來是這樣的:

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(); 
    } 
} 

我是馬克·西曼的博客的忠實讀者,並在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方法實際上是在做數據庫訪問。

東西告訴我一個對象做一個抽象工廠的創建方法內部數據庫訪問是不是一個很好的設計。

是否有任何指引,也說這是有效的一個壞主意最佳做法或東西嗎?

感謝您的幫助。

+0

顯然dataProvider.Load具有某種副作用,沿着獲取某個實例到屬性的線路?工廠方法的其餘部分實際上是否使用'dataProvider'和'someDataStructure'?如果沒有,那麼你的工廠方法的參數應該被重構爲接受它真正需要的參數。 – PatrikAkerstrand

+0

@PatrikAkerstrand:好點Patrik。實際上,由工廠創建的對象確實使用數據提供者,因爲它的Load方法確實會填充很多所需的屬性。這是第一次重構工作,但不幸的是,這是一項技術債務,可能在短期內無法獲得支付。 –

+0

好吧,請考慮這一點:如果dataProvider.Load會被多次調用,那會產生什麼問題嗎? **如果沒有**:Cool,我會將'Load'-call移動到工廠方法中,因爲它會簡化客戶端(所有對.Load的調用都將在工廠方法中)。 **如果是**:你別無選擇,只能將它放在工廠外面,因爲你不知道它是否已初始化 – PatrikAkerstrand

回答

2

通常,工廠用於解析所請求的接口或抽象類型的具體類型,因此您可以將實現的消費者與實現分離。所以通常工廠只是發現或指定具體類型,幫助解決依賴關係,並實例化具體類型並將其返回。但是,對於它可以做什麼或不可以做什麼,沒有硬性或快速的規則,但只給予它足夠的權限才能解析和實例化具體類型,這是明智的。

工廠的另一個好用處是隱藏與消費者無關的消費者類型依賴關係。例如,IExportDataProvider似乎只在內部相關,並且可以從消費者(例如ExportProcess)抽象出來。在你的榜樣

一個代碼味道,但是,如何IExportDataProvider使用。它目前似乎能夠工作的方式是,您可以獲得一次實例,但可以在後續的使用中更改其狀態(通過調用Load)。這可能會導致併發和損壞狀態的問題。由於我不知道這種類型的作用或者的實際使用情況,因此很難提出建議。在下面的示例中,我進行了調整,以便我們可以假定提供程序是無狀態的,而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. 
    } 
} 
+0

你的例子有一個問題。提供者使用不同的數據加載不同類型的幾個屬性。如果我將所有這些調用分開,以便每個方法都返回查詢的數據,那麼混凝土輸出器構造函數將具有不少參數。我知道這表明班級仍然做得太多。考慮到這些想法,你是否仍然認爲這是最好的選擇? –

+1

@SergioRomero在這種情況下,你可能會考慮一個適配器。讓工廠用於解決您需要的IExporter類型,並使用適配器實際代理IExporter的創建。我會看看我是否可以用我的意思的例子更新我的答案。 – HackedByChinese

相關問題