2

我相信我瞭解DI的基本概念定製範圍的國家/已經使用,並閱讀了大量堆棧溢出的答案,以及馬克·西曼的書夫婦的應用程序編寫IoC容器。我仍然遇到了一些麻煩的情況,特別是在將DI容器集成到一個大型的現有架構中,DI原理尚未被真正使用的時候(想想大泥球)。DI容器和遺留系統

我知道理想的場景是每個操作都有一個單一的組合根/對象圖,但是在遺留系統中,這可能沒有重大的重構是不可能的(只有新的和一些選擇重構的舊代碼部分可能具有依賴關係通過構造函數和系統的其餘部分注入,使用容器作爲服務定位器與新部件交互)。這實際上意味着,堆棧操作深處跟蹤可能包括與所撥打的電話來回新的子系統之間(單個對象圖,直到退出了一個老段)和傳統的子系統(在某些時候服務定位器調用下幾個代碼的對象圖DI容器)。

隨着(潛在的故障,我可能會得太多這還是在假定這種混合架構的完全錯誤是一個好主意),假設的方式進行,這裏的實際問題:

比方說,我們有線程池執行數據庫(或任何外部地點)中定義的各種類型的預定作業。每個單獨的計劃作業類型都是作爲繼承公共基類的類來實現的。當作業開始時,會獲得有關應該將日誌消息寫入哪些目標的信息以及應該使用的配置。配置可以通過將值作爲方法參數傳遞給任何需要它們的類來處理,但是如果作業實現比10-20個類更大,它似乎不太方便。

日誌是更大的問題。作業調用的子系統可能也需要將事情寫入日誌,並且通常在示例中這是通過在構造函數中請求ILog實例來完成的。但是在這種情況下,如果我們直到運行時才知道細節/實現,那麼它是如何工作的呢?因爲:

  • 由於在調用鏈(非DI容器控制)遺留系統段( - >有可能是多個獨立的對象圖形),子容器不能用於注入特定子範圍的定製記錄器
  • 手冊財產注入將基本上都需要進行完整的呼叫鏈(包括所有遺留子系統)進行更新

一個簡單的例子,以幫助更好地感知問題:

Class JobXImplementation : JobBase { 
    // through constructor injection 
    ILoggerFactory _loggerFactory; 
    JobXExtraLogic _jobXExtras; 

    public void Run(JobConfig configurationFromDatabase) 
    { 
     ILog log = _loggerFactory.Create(configurationFromDatabase.targets); 
     // if there were no legacy parts in the call chain, I would register log as instance to a child container and Resolve next part of the call chain and everyone requesting ILog would get the correct logging targets 
     // do stuff 
     _jobXExtras.DoStuff(configurationFromDatabase, log); 
    } 
} 

Class JobXExtraLogic { 
    public void DoStuff(JobConfig configurationFromDatabase, ILog log) { 
     // call to legacy sub-system 
     var old = new OldClass(log, configurationFromDatabase.SomeRandomSetting); 
     old.DoOldStuff(); 
    } 
} 

Class OldClass { 
    public void DoOldStuff() { 
     // moar stuff 
     var old = new AnotherOldClass(); 
     old.DoMoreOldStuff(); 
    } 
} 

Class AnotherOldClass { 
    public void DoMoreOldStuff() { 
     // call to a new subsystem 
     var newSystemEntryPoint = DIContainerAsServiceLocator.Resolve<INewSubsystemEntryPoint>(); 
     newSystemEntryPoint.DoNewStuff(); 
    } 
} 

Class NewSubsystemEntryPoint : INewSubsystemEntryPoint { 
    public void DoNewStuff() { 
     // want to log something... 
    } 
} 

我相信你通過這一點得到了圖片。

通過DI實例化老班是一個非首發,因爲很多都使用(常爲多發)構造函數注入值,而不是依賴關係,並就必須重構一個接一個。調用者基本上隱式地控制對象的生命週期,並且這在實現中被假定(它們處理內部對象狀態的方式)。

我有什麼選擇?在這樣的情況下,你還可能看到其他哪些類型的問題?試圖在這種環境中只使用構造函數注入甚至可行?

+0

我的第一個雖然關於日誌記錄是:[你是不是記錄太多?](http://stackoverflow.com/questions/9892137/windsor-pulling-transient-objects-from-the-container/9915056#9915056 ) – Steven 2012-08-15 12:03:26

回答

1

大問題。總的來說,如果只有部分代碼是DI友好的,那麼IoC容器會失去很多效果。

書籍像Working Effectively with Legacy CodeDependency Injection in .NET既要講的方式來梳理出對象和類,使DI像你描述的一個代碼庫是可行的。

獲得被測系統將是我的首要任務。我會選擇一個功能區域,其中一個與其他功能區域的依賴關係很少。

我看不出超越構造函數注入到setter注入的問題,它可以爲構造函數注入提供一個墊腳石。添加一個屬性通常比改變一個對象的構造函數的侵入性更小。