2013-04-08 63 views
0

我一直在閱讀如何編寫可測試代碼,並偶然發現依賴注入設計模式。依賴注入進一步向下「鏈」

這種設計模式是很容易理解,實在沒有什麼給它,對象請求的值而不是創建它們自己。

然而,現在我在想如何能夠使用的應用IM currenty工作我知道有一些併發症它。想象一下下面的例子:

public class A{ 

    public string getValue(){ 
     return "abc"; 
    } 
} 


public class B{ 
    private A a; 

    public B(A a){ 
     this.a=a; 
    } 
    public void someMethod(){ 
     String str = a.getValue(); 
    } 
} 

單元測試someMethod()現在很容易,因爲我可以創建一個的模擬,並有getValue()回我想做的事情。

B類的的依賴關係是通過構造函數注入,但是這也意味着A有B類外被實例化因此這種依賴已經轉移到另一個類來代替。這將會重複許多層,並且在某個點上實例化必須完成。

現在的問題是,在使用依賴注入的時候,是否不斷傳遞所有這些層次的依賴關係?這不會讓代碼更少可讀性和更多的時間來調試嗎?當你到達「頂層」時,你將如何測試該課程?

+0

這不是依賴注入。你必須在實現它之後創建接口,而不是私有A在你的類b中使用私有IAia,然後傳遞構造函數對象A並將它分配給你的變量ia – 2013-04-08 12:57:25

回答

3

嗯,是的,這意味着你必須通過所有層的依賴關係。然而,這正是Inversion of Control容器派上用場的地方。它們允許您註冊系統中的所有組件(類)。然後,您可以向IoC容器請求class B(在您的示例中)的實例,該實例會自動爲您調用正確的構造函數,以自動創建構造函數依賴的任何對象(在您的案例中爲class A)。

一個很好的討論,可以在這裏找到:Why do I need an IoC container as opposed to straightforward DI code?

4

我希望我正確地理解你的問題。

注入依賴

不,我們不會通過所有層傳遞的依賴關係。我們只將它們傳遞給與他們直接交談的圖層。例如:

public class PaymentHandler { 

    private customerRepository; 

    public PaymentHandler(CustomerRepository customerRepository) { 
     this.customerRepository = customerRepository; 
    } 

    public void handlePayment(CustomerId customerId, Money amount) { 
     Customer customer = customerRepository.findById(customerId); 
     customer.charge(amount); 
    } 
} 

public interface CustomerRepository { 
    public Customer findById(CustomerId customerId); 
} 

public class DefaultCustomerRepository implements CustomerRepository { 

    private Database database;  

    public CustomerRepository(Database database) { 
     this.database = database; 
    } 

    public Customer findById(CustomerId customerId) { 
     Result result = database.executeQuery(...); 
     // do some logic here 
     return customer; 
    } 
} 

public interface Database { 
    public Result executeQuery(Query query); 
} 

PaymentHandler不知道的Database,只會談CustomerRepository。注入Database停止在存儲庫層。

可讀性代碼

當您進行手動注入無框架或庫,以幫助,我們可能最終包含許多樣板代碼像return new D(new C(new B(), new A());這在某些時候可以少讀工廠類。爲了解決這個問題,我們傾向於使用DI框架如Guice來避免編寫這麼多的工廠。

然而,對於類,實際上做的工作/業務邏輯,他們應該更易於閱讀和理解,因爲他們只跟他們的直接合作者,做他們需要做的工作。

單元測試

我認爲通過「頂」層你的意思PaymentHandler類。在本例中,我們可以創建一個存根CustomerRepository類,並返回一個Customer對象,我們可以檢查該對象,然後將存根傳遞給PaymentHandler以檢查是否收取正確的金額。

總體思路是通過假合作者來控制他們的輸出,以便我們可以安全地斷言受測試的類的行爲(在本例中爲PaymentHandler類)。

爲什麼接口

如上註釋提到的,更優選的是依賴於接口代替混凝土類,它們提供更好的可測試性(易於嘲笑/存根)和更容易調試。

希望這會有所幫助。

1

IMO,您的問題表明您瞭解該模式。

正確使用,您將有一個Composition Root所有依賴關係被解析和注入。在這裏使用IoC容器將解決依賴關係,並將它們傳遞給您的圖層。

這與服務位置模式直接相反,服務位置模式被許多人認爲是反模式。

組合根的使用不應該讓您的代碼更少可讀/可理解,因爲設計良好的具有清晰相關依賴性的類應該是合理的自我記錄。我不確定單元測試組合根。它有一個謹慎的角色,所以它應該是可測試的。