2015-09-25 49 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)将找出在哪里以及如何创建依赖关系。

所以总结一下,一个方法是声明供应商,具有一定的定位进行注册,然后声明依赖于供应商的模块,并最终引导了整个事情在以后的某个阶段。

我觉得我有我的填充这个星期天的早晨。