2011-07-01 45 views
0

我正在研究一種參數值解析器庫。我想有一個解析器定義如下:可配置的依賴關係,容易模擬默認實現

public class Parser { 

    private ValuesConfiguration configuration; 
    private ValuesProvider valuesProvider; 
    private ValuesMapper valuesMapper; 

    public Parser(ValuesConfiguration configuration) { 
     this.configuration = configuration; 
    } 

    public Result parse(String parameterName) { 
     List<Values> values = valuesProvider.getValues(parameterName); 
     // do other stuff on values 
     // ... 
     return valuesMapper.transformValues(values, configuration); 
    } 
} 

我想這個庫的客戶不知道ValuesProvider和ValuesMapper默認的實現,並使用它像

Result result = new Parser(myConfig).parse("sampleParam"); 

雖然必須有在需要時設置自己的實現的可能性。我想知道如何以及在哪裏啓動這些默認實現,並且仍然讓客戶自行設置它們,如果他們想要的話。我不想堅持

new DefaultValuesProvider() 

等在構造函數,因爲默認實現將例如,訪問文件系統,這樣就很難測試(將它們模擬出來)。我知道我可以使用setter(比如DI),但是默認值是什麼?

編輯: 在你的所有答案後,我想最好在這裏讓setter允許客戶端提供他們自己的ValuesProvider和ValuesMapper的實現。但是如何創建默認實現?我想解耦來自邏輯的實例,所以我不想在這裏使用新的DefaultValueProvider()。工廠模式在這裏適用嗎?如果是這樣的話,我應該在哪裏使用它?

+0

相關:stackoverflow.com/questions/2045904/dependency-inject-di友好型圖書館/ 2047657#2047657 –

回答

2

如何:

public void setValuesProvider(ValuesProvider valuesProvider) { 
    this.valuesProvider = valuesProvider; 
} 

public Result parse(String parameterName) { 
    if (valuesProvider == null) { 
     valuesProvider = new DefaultValuesProvider(); 
    } 

    List<Values> values = valuesProvider.getValues(parameterName); 
    // do other stuff on values 
    // ... 
    return valuesMapper.transformValues(values, configuration); 
} 

正如馬克指出,構造函數注入可能是更好的路要走。這將如下所示:

public Parser(ValuesConfiguration configuration) { 
    this(configuation, new DefaultValuesProvider()); 
} 

public Parser(ValuesConfiguration configuration, ValuesProvider valuesProvider) { 
    this.configuration = configuration; 
    this.valuesProvider = valuesProvider; 
} 


public Result parse(String parameterName) { 
    List<Values> values = valuesProvider.getValues(parameterName); 
    // do other stuff on values 
    // ... 
    return valuesMapper.transformValues(values, configuration); 
} 

我也同意他的優點總結:

這將使你做出最終的依賴性/只讀。屬性注入對不變量提供了很弱的保證 - 例如你可以不斷改變對同一個實例的依賴,這可能不是你想要的。

然而,也有缺點:

  1. 構造的數量是可選參數的數量指數,引起了很多代表團和重複JavaDoc中的參數。因此,如果沒有可選的依賴關係,我只會使用構造函數注入。你有2,這關於我這種事情的痛苦閾值。
  2. 構造函數注入ist不是很友好的子類,因爲子類的構造函數將不得不重新聲明超級類的依賴關係。在你的情況下,希望公開所有配置選項的子類將需要4個構造函數......並且如果它添加了一個可選參數,最好不要在構造函數注入時使用這個參數,因爲這需要另外4個構造函數。因此

我的建議是使用setter注入,如果有必要,以防止相關性的重新分配,這樣做:

public void setValuesProvider(ValuesProvider valuesProvider) { 
    if (this.valuesProvider != null) { 
     throw new IllegalStateException("Dependency already set"); 
    } 
    this.valuesProvider = valuesProvider; 
} 
+0

+1但很難把它在簡歷上。 「知道如何做基本的東西」。這些日子可能是一種罕見的品質。 – irreputable

+0

更好的選擇可能是一個重載的構造函數。這將使您能夠依賴最終/只讀。屬性注入對不變量提供了很弱的保證 - 例如你可以不斷改變對同一個實例的依賴,這可能不是你想要的。 –

+0

如果您認爲惡意代碼,沒有任何東西可以爲您節省。 – irreputable

0

使用谷歌吉斯或similiar依賴注入框架:

http://code.google.com/p/google-guice/

它非常適合您的需求。您可以配置幾個模塊,描述將爲特定接口實例化哪些實現。例如,您可以有一個ProductionModule和UnitTestModule。

+0

客戶需要依賴Guice。如果客戶使用Spring會怎麼樣? DI框架不能很好地共存。如果Guice被用作服務定位器,那麼**模塊就會在全球範圍內達成一致。也可能有一個全球工廠,可以由客戶重新分配。 – irreputable

+0

您也可以使用彈簧。 Spring和Guice具有相似的功能。 Spring的ApplicationContext我相信提供了Guice的模型功能(參見例如http://blog.springsource.com/2007/05/14/annotation-driven-dependency-injection-in-spring-21/)。吉斯是更清潔恕我直言。依賴注入比工廠強大得多。更少的樣板,巨大的靈活性非常好,可讀的代碼。一旦你嘗試了,就沒有回頭路了。 –

+0

OP正在製作一個庫,要求客戶使用特定的DI框架是錯誤的。 – irreputable