我目前正試圖學習使用IoC容器的好處並熟悉DI。我開始使用StructureMap,因爲它看起來很簡單而且功能強大。我是否正確理解DI/IoC?
我想驗證一下我對這些概念的理解是否正確。讓我們假設在應用程序中以下基本的類(中省略了簡潔的細節):
public class OrderService : IOrderService
{
private IOrderRepository _repository;
private ITaxCalculator _taxCalculator;
private IShippingCalculator _shippingCalculator;
public OrderService(IOrderRepository repository,
ITaxCalculator taxCalculator,
IShippingCalculator shippingCalculator)
{
this._repository = repository;
this._shippingCalculator = shippingCalculator;
this._taxCalculator = taxCalculator;
}
public IOrder Find(int id) { return this._repository.Find(id); }
}
public class OrderRepository : IOrderRepository
{
public IOrder Find(int id) { // ... }
}
public class StandardShippingCalculator : IShippingCalculator
{
// ...
}
public class StandardTaxCalculator : ITaxCalculator
{
private ITaxSpecification _specification;
public StandardTaxCalculator(ITaxSpecification specification)
{
this._specification = specification;
}
}
首先,依賴倒置原則規定,因爲OrderService是一個「高級別」模塊它houldn't取決於任何較低級別的實現細節,它應該只有那些類的引用,並且能夠請求他們做他們的事情,而不必知道它正在做什麼,並且消費代碼應該負責創建和處理這些預配置的模塊到它。是對的嗎?因此,DI保持這些類鬆散耦合,以便他們不必知道調用該依賴關係的方法的確切方式,只需調用將並執行所需的任何操作 - OrderService不會注意存儲庫是查詢XML,還是使用NHibernate或EF甚至原始數據集;它只知道它可以調用存儲庫,告訴它找到一個ID爲42的訂單,並且存儲庫將知道該做什麼。
這也是我的理解,在這種情況下,一個IoC容器,StructureMap提供了一個好處,不會強迫我們確保我們手動創建所有這些依賴項並將它們傳入。例如,Main方法使用上面的代碼瑣碎的應用程序可能有:
static void Main(string[] args)
{
IOrderService service = new OrderService(
new OrderRepository(), new StandardShippingService(),
new StandardTaxService(new StandardTaxSpecification()));
IOrder order = service.Find(42);
// Do something with order...
}
這是與所有的新聞來設置它revoltingly難看;即使我創建了變量,它仍然很難看。使用IoC容器的讓我避免了這一切,並在StructureMap的情況下,它會變成:
static void Main(string[] args)
{
ObjectFactory.Initialize(x =>
{
x.For<IOrderRepository>().Use<OrderRepository>();
x.For<IOrderService>().Use<OrderService>();
x.For<IOrder>().Use<Order>();
x.For<IShippingCalculator>()
.Use<StandardShippingCalculator>();
x.For<ITaxCalculator>().Use<StandardTaxCalculator>();
x.For<ITaxSpecification>()
.Use<StandardTaxSpecification>();
});
IOrderService service =
ObjectFactory.GetInstance<IOrderService>();
IOrder order = service.Find(42);
// do stuff with order...
}
這是更清潔,更容易維護,並讓我剛剛SUB OUT具體的類,比如說,如果我正在編寫單元測試,那就是模擬。簡而言之,它的好處是它將所有的東西都解耦到了我甚至不需要關心的地方(在調用代碼中,也就是說)一個特定的類依賴於什麼,我可以使用容器創建一個並讓它執行這是事情,並確保消費代碼只知道它需要什麼 - 例如,如果控制器正在調用服務,在真正的應用程序中,它不需要知道存儲庫或計算器或規格,所有它需要知道的是使用OrderService對訂單執行某些操作。
這是否理解正確?關於這一點,有幾件事情我不知道的還:
如果你決定使用IoC容器,是不是意味着要在應用程序中使用隨處可見,只有當你有很多倒依賴關係來解決,還是僅在消費者?例如,在OrderRepository中,如果我使用具體的實現並創建訂單;這個類是否也會使用StructureMap來獲取訂單?這可能有些愚蠢的問題,但我所見過的所有DI/IoC示例只着重於在消費客戶端(例如網頁)中使用它,並且從不涉及在其他地方使用它。看起來它是一種全有或全無的方法:如果您打算使用IoC容器,那麼它在各處都會使用;你基本上是與更換任何調用
new SomeObject();
,在這種情況下,ObjectFactory.GetInstance<ISomeObject>();
是否認爲好或壞有每一個類(如果可能的話,當然)從接口是否有必要使用DI派生/ IoC或類似嘲笑它?我見過很多代碼示例,其中每個不是內置類的類都有一個接口,而我可以看到這樣做的好處和可能的未來證明,我認爲繼TDD或BDD之後可能是因爲使用這些方法學通常會告訴你,如果你需要一個類的接口,但我已經看到許多TDD或不知道的人,他們認爲你永遠不應該把一個對象的類型定義爲一個具體的類;它應該總是是一個接口或抽象類作爲基礎類型。這似乎是「不必要的複雜性」代碼味道的情況,更不用說違反了YAGNI。
好問題 - 雖然韋恩有點多讀 - 我不是說這是消極的方式,但我不知道有多少人會經歷這一切。我會盡力的。 – 2011-01-05 19:17:25
是的,當我打字的時候,我會被帶走,因爲我試圖解釋很多,並給出我正在談論的實例:) – 2011-01-05 19:23:13
沒問題 - 你絕對做了很棒的工作,提供例子等。 – 2011-01-05 19:48:30