2

我知道注入一個IoC容器作爲依賴通常被認爲是一件壞事,因爲它基本上與Service Locator反模式相同。然而,有一種情況我認爲它可能是有道理的,尤其是因爲我沒有看到一個簡單的方法。注入IoC容器作爲依賴有多糟糕?

考慮這些類:

// domain class 
public class Foo 
{ 
} 

// ViewModel 
public class FooViewModel 
{ 
    [Dependency] 
    public IBar Bar { get; set; } 

    [Dependency] 
    public IBaz Baz { get; set; } 

    private readonly Foo _foo; 

    public ViewModel(Foo foo) 
    { 
     _foo = foo; 
    } 
} 

// Dependencies used by ViewModel 
public interface IBar { void Frob(); } 
public interface IBaz { void Fiddle(); } 

現在假設我要顯示FooViewModel列表,從Foo列表建成。要從Foo創建FooViewModel一個例子,我有以下選擇:

  • 只是調用構造函數和手動設置的依賴:

    var vm = new FooViewModel(foo) { Bar = _bar, Baz = _baz }; 
    

    這是不切實際的,因爲:

    • 創建FooViewModel的對象需要具有BarBaz作爲依賴關係,即使它使用他們只創建FooViewModel
    • 如果我添加一個新的依賴FooViewModel,我將不得不更新我創建它的一個實例的代碼。

    這兩個問題都可以通過創建工廠來解決,但該工廠需要編寫和維護。在這個非常基本的場景中,這不是一個問題,但對於一個大型的真實世界的應用程序來說,如果有許多依賴關係很多的工廠,它會變得非常麻煩。

  • 使用IoC容器來解決FooViewModel

    var vm = _container.Resolve<FooViewModel>(new ParameterOverride("foo", foo)); 
    

    這需要依賴的照顧,但後來我不得不使用容器在我的視圖模型層,這是壞的(是嗎?)。

我在想一個折衷:創建一個工廠類,只有容器的依賴,並用它來建立FooViewModel

public class FooViewModelFactory 
{ 
    [Dependency] 
    public IUnityContainer Container { get; set; } 

    public FooViewModel CreateFooViewModel(Foo foo) 
    { 
     return Container.Resolve<FooViewModel>(new ParameterOverride("foo", foo)); 
    } 
} 

這樣,我有一個非常簡單的工廠這很容易維護,而且只有容器作爲依賴項的類是工廠,而不是實際的業務相關代碼。

您是否看到這種方法的缺點?我錯過了一個更好/更簡單的解決方案嗎?

回答

0

我最終採用了類似於我在我的問題表明的方法:

public class FooViewModelFactory 
{ 
    [Dependency] 
    public IUnityContainer Container { get; set; } 

    public FooViewModel CreateFooViewModel(Foo foo) 
    { 
     var vm = new FooViewModel(foo); 
     Container.BuildUp(vm); 
     return vm; 
    } 
} 

採用BuildUp使得代碼比使用ParameterOverride更具可讀性。

使用容器作爲依賴可能是一個代碼味道,但在這種情況下,它肯定使代碼更簡單,我沒有注意到,在使用這種方法的幾個月的任何缺點。(但我只在工廠注入容器)。

理想情況下,BatteryBackupUnit建議的方法本來會更好,但是如果沒有運行時代碼生成,這是不切實際的,而且在編譯時生成代碼太多,所以我選擇了更實用的方法。

0

如果所有的視圖模型可以與IBarIBaz相同的實例生活,那麼你可以簡單地定義一個工廠,擁有這些實例作爲依賴關係:

public class FooViewModelFactory 
{ 
    private readonly IBar _bar; 
    private readonly IBaz _baz; 

    public FooViewModelFactory(IBar bar, IBaz baz) 
    { 
     _bar = bar; 
     _baz = baz; 
    } 

    public IEnumerable<FooViewModel> CreateViewModels(IEnumerable<IFoo> foos) 
    { 
     return foos.Select(f => new FooViewModel(f) {Bar = _bar, Baz = _baz}); 
    } 
} 

我看不出有什麼不好手動實例化視圖模型,因爲您必須通過IFoo的不同實現。

+0

謝謝,但我認爲你錯過了我的問題的重點......我已經在我的問題中提到過這種方法,問題是如果您有很多依賴關係,很難維護。我正在尋找的是關於使用IoC容器的反饋。 –

1

我同意服務定位器是一種反模式。

這就是說,你發佈的問題只能由你自己回答,實際上並不適合Q & Stackoverflow的格式:這是基於高度的意見。這可能是更好的選擇https://softwareengineering.stackexchange.com/

決定這一點的重要一點是評估和理解所有可能的解決方案及其副作用。副作用還可能包括對維護代碼的人員的擔憂。他們有能力理解它嗎?他們如何工作?你可以教育他們嗎?

所以我收集你應該問的問題是:這個問題怎麼能解決呢? 然後嘗試一下,親自看看解決方案的比較。

對諸如「不這樣做」(沒有進一步的解釋和理解)的提示太過重視只會導致......沒有增長!這基本上是最糟糕的事情發生(不學習任何東西=>總是不好的設計)。

爲了您的具體問題,我想有可能的解決方案在球場:

使用自動生成的工廠。這與您建議的工廠類型非常相似,但它將您的編碼限制爲您想要提供的參數。所有其他人你不必關心。 例如,Ninject擁有工廠的擴展,你可以做的東西,如:

public class FooViewModel 
{ 
    public FooViewModel(Parameter parameter, Bar bar, Nice nice) { ...} 
} 

public interface IFooViewModelFactory 
{ 
    FooViewModel Create(Parameter parameter); 
} 

kernel.Bind<IFooViewModelFactory>().ToFactory(); 

FooViewModel instance = kernel.Get<IFooViewModelFactory>() 
     .Create(new Parameter("my parameter")); 

這基本上你得到維護的代碼。添加/更改/刪除依賴關係@FooViewModel不會導致工廠更改(除非實際確實需要提供參數)。

的缺點可以,即對象圖的一部分 - 包括依賴 - 僅實例化時工廠.Create(..)方法被調用。這取決於自動生成代碼的實現方式。 在理想的依賴注入中,對象圖是在組合根,應用程序開始時構建的,並且所有內容都是一次構建的。這樣,你會發現IOC配置問題時,馬上啓動該應用程序,而不是稍後當你使用特定的功能,然後點擊按鈕的一些 - 這將調用一些工廠。

據我所知統一有某種延伸從Castle.Windsor移植 - TecX.Unity.Factories,請參閱http://outlawtrail.wordpress.com/2012/07/27/auto-generated-factories-with-unity/。 然後還有http://www.nuget.org/packages/Unity.TypedFactories,這基本上是一樣的。

你甚至可能是內容與團結的「自動工廠」的支持(Func<>)。我其實不知道它是否能夠處理參數,我對自己的團結相當陌生。

+0

感謝您的回答。你是對的,這個問題可能會更適合Programmers.SE或CodeReview.SE。我喜歡IoC容器生成的工廠的想法,我會研究它! –

+0

好吧,生成的工廠不會爲我工作,不幸的是...我的應用程序是一個Windows應用商店應用程序,並且'System.Reflection.Emit'在此平臺上不可用,這意味着動態代碼生成僅限於' System.Linq.Expressions';不足以產生新的類型...我可以嘗試自動生成'Func <>',但它不完全相同;我的一些工廠有不止一種方法。 –

+0

那麼,如果你願意投入一些工作,你可以看看https://github.com/BrunoJuchli/Unity.StaticProxyExtension(免責聲明:我是作者),這是專門建立的。我還沒有想辦法創建Unity工廠生成器。如果您願意嘗試一下,請在github問題跟蹤器上創建一個問題,詳細說明界面的外觀。我可能會在本週末寫下來。 – BatteryBackupUnit