6

我一直在嘗試使用命名委託而不是單一方法的接口。這對代碼的大小一定的優勢,因爲我們可以從(一些換行符刪除,以免誇大的情況下)去:有沒有簡單的方法來註冊Castle Windsor的靜態封閉?

public interface IProductSource 
{ 
    IEnumerable<Product> GetProducts(); 
} 
public class DataContextProductSource : IProductSource 
{ 
    private readonly DataContext _DataContext; 
    public DataContextProductSource(DataContext dataContext) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     _DataContext = dataContext; 
    } 
    public IEnumerable<Product> GetProducts() 
    { 
     return _DataContext.Products.AsEnumerable(); 
    } 
} 

到:

public delegate IEnumerable<Product> DGetProducts(); 
public static class DataContextFunctions 
{ 
    public DGetProducts GetProducts(DataContext dataContext) 
    { 
     if (dataContext == null) throw new ArgumentNullException("dataContext"); 
     return() => dataContext.Products.AsEnumerable(); 
    } 
} 

這基本上是走的事實的優點:一旦你用依賴注入進行得足夠遠,許多類就不再僅僅是關閉了。這些類可以被返回lambda表達式的函數替換。然後可以將整套相關函數(不需要封裝任何可變狀態,但是已經使用「標準」依賴注入中的類表示)集中到靜態類(或VB中的「模塊」)中, 。

這一切都很好,但我很難找到與Castle Windsor一起註冊這些靜態方法的最佳方法。不依賴的方法很容易:

Component.For<DGetIntegers>().Instance(Integers.GetOneToTen) 

但是我們的DataContextFunctions.GetProducts從上面有一些依賴關係。我發現註冊這個最好的辦法是:

Component.For<DGetProducts>().UsingFactoryMethod(
    kernel => DataContextFunctions.GetProducts(kernel.Resolve<DataContext>()) 

這可以得到相當冗長,顯然不必直接請求內核每個依存性的那種失敗的點位。在我看來,容器應該需要的所有靜態信息都是可用的,所以應該可以做到這一點。

現在的問題是,Castle Windsor(或任何其他容器)是否有簡單的方法來做到這一點,我已經錯過了,或者出現了技術問題,或者它只是一個小例子包括在內呢?

回答

4

這是一個有趣的方法。我想你可以很容易地使用自定義激活器來工作。

+0

謝謝,Krzysztof。我會在某個時候嘗試一下並報告結果。任何鏈接的好地方開始? – ninjeff

+0

@ninjeff如果你在2.5.x文檔應該讓你開始docs.castleproject.org/(S(hucszcu5ilznbv45fvrim355))/...如果你在3.0測試版,有一些API的變化,雖然概念仍然是相同。讓我知道如果DOCO是不完美的 –

+0

我能夠得到這個工作。我會在一個小時內用我自己的答案發布鏈接和描述(像我這樣的低業力用戶無法在八小時內回答我們自己的問題)。 – ninjeff

10

簡短的回答

你試圖使DI容器(溫莎城堡)執行功能成分,但它實際上是針對對象組成。這隻會給你帶來很大的摩擦。我的猜測是你會得到與其他容器相同的體驗。

DI容器是圍繞面向對象的概念設計的 - 特別是SOLID。他們在這些原則下工作得非常好,因爲他們的設計理念是固有的,例如構造器注入和自動佈線等。

更多功能的方法沒有什麼錯,但我還沒有看到圍繞函數組合構建的DI容器而不是對象組合。

龍答案

爲DI的一般原則使用代表往往是靜態類型語言問題(至少在.NET)的一對夫婦的原因。從概念上講,這種方法沒有什麼問題,因爲delegate can be considered as an anonymous Role Interface。然而,由於類型模糊性,它往往變得笨拙。

我通常看到的最常見的方法是使用BCL的內置代表,如Func<T>Action<T>等。但是,您可能有許多不同的消費者依賴於Func<string>,在這種情況下,事情變得模糊 - 僅僅因爲消費者需要Func<string>並不意味着他們需要委託人具有相同的角色。儘管在代理中使用DI機械可能會發生的是代表隱藏應用程序角色。角色消失,只剩下機械師。

然後,您可以,如OP建議定義自定義委託每個角色,如建議通過此委託:

public delegate IEnumerable<Product> DGetProducts(); 

不過,如果你採取的路線,然後什麼也不獲得。還必須爲每個角色定義「角色委託」。對比一下定義一個類似的界面,它應該是清楚的,唯一的儲蓄是一對夫婦的角度支架和一個明確的方法定義:

public interface IProductSource { IEnumerable<Product> GetProducts(); } 

這不是一個很大的開銷(如果有的話)。

你也可能想看看這個討論:http://thorstenlorenz.wordpress.com/2011/07/23/dependency-injection-is-dead-long-live-verbs/

+0

感謝您的回覆,馬克。實際上我並沒有試圖去完全實用的方法,而是嘗試一種混合方法。試圖感受各種概念使用函數和對象的優點和缺點,並且看看它們如何交互。 – ninjeff

+0

關於接口的開銷,它更多的是實現類的長度,而不是接口本身。 – ninjeff

1

基於Krzysztof的指針,我能夠編寫一個自定義激活器來處理這個問題。來源是github

寄存器靜態代表這樣的(以下在討論的示例):

container.Register(Component. 
    For<DGetProducts>(). 
    ImplementedBy(typeof(DataContextFunctions)). 
    Named("GetProducts"). 
    Activator<StaticDelegateActivator>()); 

代碼很大程度上是DefaultComponentActivator重寫。我還需要將DependencyTrackingScope複製粘貼出Windsor源代碼,因爲它是內部的。

測試項目中的單個「測試」僅涵蓋單個用例。我不懷疑它可以用於代理等更高級的場景。

我接受了Krzysztof的回答,因爲他的指導導致瞭解決方案。

相關問題