6

我正在開發使用DI和IoC的可擴展框架。用戶必須能夠通過將自己的實現放入容器來覆蓋框架內的現有功能。如何避免與IoC容器耦合

我該如何讓用戶做到這一點而不要求他們知道我正在使用哪個IoC容器?

我的當前中途的解決方案是構建我的組件如下:

1)定義只包含接口抽象組件。

2)定義實現這些接口的混凝土組件。用戶可以自行定義以覆蓋現有的功能。

3)在獨立的程序集中定義容器綁定;即每個混凝土組件的一個裝訂組件。

這意味着混凝土組件不與特定的IoC容器耦合,並且如果我使用了不同的容器,它們將被禁止更改。但是,用戶仍然需要知道我的框架使用哪個容器來編寫綁定程序集,如果我更改了IoC容器(即從Ninject到Spring),他們需要釋放新的綁定程序集。

我錯過了什麼嗎?

+0

這種方法的問題在於,您受限於容器可以支持的最低公分母,在很多情況下,這不會滿足您的內部需求。我已經嘗試了這種確切的方法,並且對於剛剛接觸該項目的用戶而言造成了太多困惑。 – 2011-04-26 13:59:13

+0

你用什麼來代替? – 2011-04-26 14:38:32

+0

該框架提供了一套明確的註冊點,並在沒有容器的情況下管理內部依賴關係。註冊點包括工廠方法(通常以Func 爲基礎)。然後,容器擴展(在單獨的程序集中)可以註冊併爲容器提供工廠方法。還可以使用註冊的中間模型將元數據從容器傳輸到框架。 – 2011-04-26 17:44:11

回答

1

通用服務定位器是一種方法,但它只包含解析方法,不用於註冊。

你可能想看看在agatha-rrsl project中這是如何實現的。有一個更完整的解釋here,但在短:

  • 定義一個容器無關的接口,用於註冊和解析類型
  • 爲不同的容器提供實現(或讓用戶提交的實現)

警告:您可能無法直接在您的圖書館中使用您選擇的容器。

+0

我以前玩過這個想法;知道別人正在這樣做,似乎更容易接受:) – 2011-04-26 12:55:41

+0

Aww ...這不是一個好的解決方案。我會自動解僱使用任何使用Service Locator反模式的Fx:http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx – 2011-04-26 13:09:02

+0

@Mark Seemann爲什麼你認爲這是這種情況?服務定位器作爲一種反模式對手頭問題是一個正交關係。此外,Agatha不認可/依賴/推廣服務定位器... – jeroenh 2011-04-26 13:35:41

2

常用的方法是抽象的容器common service locator

作者的MvcExtensions抽象出的IoC遠很成功。

+0

公共服務定位器界面是隻讀的;你不能使用它註冊服務。 – 2011-04-26 12:03:33

0

兩個簡單的選擇是爲用戶提供一些字典,他們可以在其中註冊類型映射,或者爲他們提供一個容器界面,提供您認爲可能需要的所有服務,並允許用戶提供他們自己的包裝容器。道歉,如果這些不太適合你的情況,我主要使用Unity,所以我不知道Ninject等Unity做不了的奇特事情。

0

我已經通過使用屬性解決了這個問題,並提供了一個掃描器類,用於查找所有實現和它們提供的接口。

public class ComponentAttribute : Attribute 
{} 

// class that should be registered in the container. 
[Component] 
public class MyService : IMyService 
{} 

// Contains information for each class that should be 
// registered in the container. 
public interface IContainerMapping 
{ 
    public Type ImplementationType {get;} 
    public IEnumerable<T> ImplementedServices {get; } 
} 

public class ComponentProvider 
{ 
    public static IEnumerable<IContainerMapping> Find() 
    { 
     var componentType = typeof(ComponentAttribute); 
     foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) 
     { 
      if (type.GetCustomAttributes(componentType, false).Count == 0) 
       continue; 

      var mapping = new ContainerMapping(type); 
      List<Type> interfaces new List<Type>(); 
      foreach (var interfac in type.GetInterfaces()) 
      { 
      //only get our own interfaces. 
      if (interface.Assembly != Assembly.GetExecutingAssembly()) 
       continue; 

      interfaces.Add(interfac); 
      } 

      mapping.ImplementedServices = interfaces; 
      yield return mapping; 
     } 
    } 
} 

該解決方案爲用戶提供了很大的靈活性。他可以直接使用[Component]屬性或使用您的解決方案來提供自己的解決方案。

哪些用戶應該做的是一樣的東西:

foreach (var mapping in ComponentProvider.Find()) 
    myContainer.Register(mapping.ImplementationType).As(mapping.ImplementedServices); 

我通常通過提供登記在我最喜歡的容器一切MyProject.autofac項目創建一個全現成的解決方案。

9

Write loosely coupled code。應用程序應該依賴於容器。 Frameworks不應該。

+0

雖然我很欣賞你的觀點,但我覺得要求用戶在作曲的根部綁定所有內容是不合理的。當然,他們應該定義什麼時候組合出現,但是不是有利於在模塊內定義預設綁定,然後用戶可以使用他們自己的DI容器來調用這些綁定?如果我的框架V2需要額外的子系統服務,該怎麼辦?要求用戶更新他們的組合根是否公平? – 2011-04-26 13:41:50

+0

您可以提供合理的默認值。沒有理由強迫用戶做任何事情,但可以擴展。 – 2011-04-26 13:43:48

+0

這使我回到原來的問題:如何在保持容器不可知的情況下定義合理的默認值?綁定是針對容器定義的... – 2011-04-26 13:48:14