對象創建是生命週期管理問題的一部分。這通常是您要分開處理的責任。常見的模式是:
- 依賴注入
- 建設者,抽象工廠或工廠方法
- 單,原型或對象池
對這些念起來看,例如谷歌結果creational patterns和dependency injection。
我將在這裏關注依賴注入,因爲它是匹配您的示例和結果的最簡單方法,它具有乾淨的可測試代碼。其他最喜歡的模式是建設者或工廠。
依賴注入(或控制反轉)是一種非常常見的模式(雖然通常不稱爲模式,我認爲它是一種)。提供D.I.最好的框架是Spring或Java自己的CDI,但您也可以手動完成,如下所示。
構造器注入
在您的例子一個簡單的使用依賴注入的可能像這樣工作:
public Class Dependency {
}
public Class Application {
private final Dependency dependency;
public Application (Dependency dependency) {
this.dependency = dependency;
}
}
用法:
public static void main(String[] args) {
Application application = new Application(new Dependency());
}
注意Application
也沒有創造Dependency
但預計將提供一個Dependency
的實例通過構造函數。這被稱爲構造函數注入。這種方式的優點是你可以使依賴性字段最終。一個缺點是,在許多依賴關係下,構造函數可能會得到太多爭論以保持可理解性。爲了避免這種情況,你可以使用生成器模式,或使用setter注入:
Setter注入
public Class Dependency {
public String doStuff(String input) {
return input + " stuffed";
}
}
public Class Application {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
public String letsGo(String input) {
String stuff = dependency.doStuff(input);
return "I called my dependency and it said: " + stuff ;
}
}
用法:
public static void main(String[] args) {
Application application = new Application();
application.setDependency(new Dependency());
System.our.println(application.letsGo("rabbit"));
}
輸出:
I called my dependency and it said: rabbit stuffed
測試
現在關於可測試性。請記住,在單元測試中,我希望能夠在不測試類Dependency
的情況下測試類Application
。如果Application
正在創建它自己的Dependency
的實例,這將很難做到。使用依賴注入很容易。下面是使用JUnit和使用的Mockito註解,這是非常標準的例子:
@RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {
@InjectMocks
private Application instance;
@Mock
private Dependency dependency;
@Test
public void test() {
// SETUP
String testStuff = "some test stuff";
String input = "Micky";
when(dependency.doStuff(input)).thenReturn(testStuff);
// CALL
String actualResult = instance.letsGo(input);
// VERIFY
verify(dependency).doStuff(input);
String expectedResult = "I called my dependency and it said: " + testStuff;
assertEquals(actualResult, expectedResult);
}
}
這裏是的Mockito創造Application
你的一個實例,並注入與@Mock
進去註釋任何領域。它會自動決定通過構造函數或setter來實現。該行:
when(dependency.doStuff(input)).thenReturn(testStuff);
指示模擬Dependency
在調用時返回特定值。這意味着我們不會調用真正的Dependency
代碼,而是要求Mockito在調用該類時生成該類的假結果。
調用Application
實例後,你可以檢查到依賴預期的調用按如下方法制備:
verify(dependency).doStuff(input);
的Mockito檢查指定的與會者呼籲只有一次,併產生一個錯誤,如果這不是案件。
然後使用assertEquals
將實際結果與預期結果進行比較。如果值不相等,則在運行測試時,JUnit會生成一個錯誤。
因此,所有這一切的主要教訓是,對象創建是一個重要的關注點,理想情況下應該使用創建模式或依賴注入來處理對象。通過這樣做,您可以集中精力完成主要任務,使其更易於理解和測試。
備註
直噴本身可能不被視爲一種模式,因爲它不夠具體 - 您可以通過多種方式來構建它。儘管如此,無論何時你看到代碼創建它自己的依賴關係的實例,你都應該考慮到代碼的味道並嘗試重構它。
D.I.的春季實施使用稱爲應用程序上下文的抽象工廠。彈簧支持注射通過簡單地註釋字段與@Autowired
(或@Inject
):
@Autowired
private Dependency dependency;
的Dependency
實例由彈簧從它通常是與@Configuration
註釋一個XML file或類應用上下文而獲得。在應用程序上下文中,您可以指定存在哪些依賴項(bean)以及應該如何設置和連接在一起。
在聲明時實例化是避免空指針異常的常見做法。 但是,在這種情況下,我認爲這並不重要,因爲無論如何你都在實例化它。但前一種方法可以讓你跳過構造函數定義... – Jay
檢查[此anwser](http://stackoverflow.com/questions/4916735/default-constructor-vs-inline-field-initialization) –
取決於事實是否當你創建一個新的'B'時,你需要不同的'A' – XtremeBaumer