2015-09-25 48 views
0

我是清理一些代碼的過程。我決定這麼做,因爲很可能在不久的將來會要求提供新功能,並且代碼很難理解(不僅因爲它使用依賴容器作爲服務定位器)。從服務定位器轉換爲依賴注入

在相當程度上,我明白了爲什麼SL是可怕的依賴管理,我已經宣佈在操作的簽名的方法,甚至之前,我知道什麼是OOP的依賴關係。

當像AngularJS框架的工作,你依賴注入是免費的。我目前的項目並非如此。我認爲實例化應用程序「正確的方式」的唯一方法是通過服務定位主要方法中的所有內容。請指出我朝着更優雅的依賴注入實現的正確方向。

這是我目前的做法:

function main() 
    container = new Dic 
    container->set(A, lazyNew(foo/bar/A) 
    container->set(B, lazyNew(A), depends-on:[A]) 
    container->set(App, lazyNew(App), depends-on:[A, B]) 
    // more configuration code 
    app = container->get(App) 
    app.main() 

這裏的缺陷是,我仍然使用容器作爲一個服務定位器。唯一的好處是依賴關係圖是「自動解析」的(每個實例的冗長配置和依賴關係聲明)。另一個親是實例化發生在一個地方。請幫助我瞭解如何把這個帶到真棒的下一個級別。

回答

1

由於您僅使用主方法中的容器,因此您將而不是使用該容器作爲服務定位器。

,如果您使用您的類中的容器(而不是主類),它只會被認爲是服務的位置。

其實,做依賴注入的時候,我們必須創建(對於其他類型的應用程序或其他入口點),我們的主要方法的對象圖。這個地方叫做Composition Root

問:您是否在使用構造器注入類中的?我假設你這樣做。

在我看來,一個更優雅的DI(或組合根)實現是不使用DI容器,而是使用Pure DI。看到我的文章here有一個原因。

UPDATE

這裏是你如何使用構造器注入的例子。

我將使用C#語言。

public interface IDependencyA {} 
public interface IDependencyB {} 
public interface IDependencyC {} 
public interface IBackEnd {} 

public class DependencyC : IDependencyC {} 
public class DependencyA : IDependencyA {} 

public class DependencyB : IDependencyB 
{ 
    private readonly IBackEnd m_BackEnd; 
    public DependencyB(IBackEnd back_end) 
    { 
     m_BackEnd = back_end; 
    } 
} 

public class BackEnd : IBackEnd 
{ 
    private readonly IDependencyC m_DependencyC; 
    public BackEnd(IDependencyC dependency_c) 
    { 
     m_DependencyC = dependency_c; 
    } 
} 

public class App 
{ 
    private readonly IDependencyA m_DependencyA; 
    private readonly IDependencyB m_DependencyB; 
    public App(IDependencyA dependency_a, IDependencyB dependency_b) 
    { 
     m_DependencyA = dependency_a; 
     m_DependencyB = dependency_b; 
    } 
} 

在這裏你可以如何使用統一的容器來建立你的對象圖:

UnityContainer container = new UnityContainer(); 

container.RegisterType<IDependencyA, DependencyA>(); 
container.RegisterType<IDependencyB, DependencyB>(); 
container.RegisterType<IDependencyC, DependencyC>(); 
container.RegisterType<IBackEnd, BackEnd>(); 

App application = container.Resolve<App>(); 

這裏是你如何使用純DI來創建對象圖:

App application = new App(
    new DependencyA(), 
    new DependencyB(
     new BackEnd(
      new DependencyC()))); 
+0

我第一句不同意,因爲即使我使用的容器來定位「應用程序」在一個地方,它仍然用作一個服務到位的定位。除了App的預先配置的依賴關係之外,什麼都沒有被注入。如果我要爲該項目添加一個後端(dep,dep,dep,...),我需要從容器中獲取(定位服務)它。我不明白我怎麼可以推廣注入依賴到一個構造函數的過程。感謝您的鏈接。 –

+0

你不應該從容器中獲得'BackEnd',只有在你的情況下是'App'的根類。你用容器註冊'BackEnd',然後在你的對象圖中的某個地方註冊一些類,例如通過構造器注入來依賴'BackEnd',然後容器將注入BackEnd到對象圖中的適當位置。 –

0

在一般條款,您需要設備的某種基礎設施來聲明依賴關係並抽象聲明的服務或模塊的實例化。

鑑於角例中的問題,如果你退後一步,並在其上反映,一切都在通過角的angular.module功能發生。

直觀上,這必須如何「應用程序」和後來的應用程式(指令,服務,控制器)的更小的組件聲明和與底層的依賴注入容器中註冊。畢竟,所有這些需要依賴數組作爲第二個參數:

var injectable3, injectable2, injectable; 

injectable = angular.module( 'myApp', [dependency, ..., fn(d1, ...){}]); 
injectable2 = injectable.controller('foo', [dependency, ..., fn(d2, ...){}]); 
injectable3 = injectable.service( 'bar', [dependency, ..., fn(d3, ...){}]); 

這似乎不可思議,但它真的不是。他們都通過某種依賴注入容器進行交談。 loader.js

審視一些核心方法的角度:這是在的意見實際上指出:

/** 
* @name angular.bootstrap 
* @returns {auto.$injector} Returns the newly created injector for app. 
*/ 
function bootstrap(element, modules, config) { 
    // (...) 
    var doBootstrap = function(element, modules, config) { 
     modules = modules || []; 
     modules.unshift(['$provide', function ($provide) { 
      $provide.value('$rootElement', element); 
     }]); 
     if (config.debugInfoEnabled) { 
      modules.push(['$compileProvider', function ($compileProvider) { 
       $compileProvider.debugInfoEnabled(true); 
      }]); 
     } 
     // (...) 
     modules.unshift('ng'); 
     var injector = createInjector(modules, config.strictDi); 
     injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', 
       function bootstrapApply(scope, element, compile, injector) { 
        scope.$apply(function() { 
         element.data('$injector', injector); 
         compile(element)(scope); 
        }); 
       }] 
     ); 
     return injector; 
    }; 
    // (...) 
    if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { 
     return doBootstrap(); 
    } 
    // (...) 
} 

如果不記得,引導方法負責初始化應用程序,如<body ng-app="coffeeShop">。上面的摘錄說明了爲每個應用程序聲明附加的依賴關係。下一步(createInjector)將找出在哪裏以及如何創建依賴關係。

所以總結一下,一個方法是聲明供應商,具有一定的定位進行註冊,然後聲明依賴於供應商的模塊,並最終引導了整個事情在以後的某個階段。

我覺得我有我的填充這個星期天的早晨。