13

东西一直在窃听我,因为我在另一个stackoverflow问题(精确的一个现在回避了我)读到一个答案,其中用户说类似“如果你'我打电话给服务定位器,你做错了依赖注入与Ninject,MVC 3和使用服务定位器模式

这是一个有很高声誉的人(我觉得在成千上万的人中),所以我倾向于认为这个人可能知道他们在说什么。自从我第一次开始学习它以来,我一直在使用DI作为我的项目,它与单元测试有什么关系,什么不是。这是我现在很舒服,我认为我知道我在做什么。

但是,有很多地方我一直在使用服务定位器来解决项目中的依赖关系。一旦主要的例子来自我的ModelBinder实现。

典型模型活页夹的示例。

public class FileModelBinder : IModelBinder { 
    public object BindModel(ControllerContext controllerContext, 
          ModelBindingContext bindingContext) { 
     ValueProviderResult value = bindingContext.ValueProvider.GetValue("id"); 

     IDataContext db = Services.Current.GetService<IDataContext>(); 
     return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue); 
    } 
} 

不是一个真正的实现 - 只是一个简单的例子

由于ModelBinder的实施需要一个新的实例时,将粘合剂第一要求,这是不可能的使用依赖注入的构造函数此具体实施。

这是很多我的课程。另一个例子是Cache Expiration过程,只要缓存对象在我的网站中到期,它就会运行一个方法。我运行一堆数据库调用,而不是。我也使用服务定位器来获得所需的依赖关系。

另一个问题最近我(我张贴的问题就在这里约)是我所有的控制器需要我用DI为IDataContext的实例 - 但一个操作方法需要IDataContext的不同实例。幸运的是,Ninject以一种有名的依赖来救援。但是,这感觉就像一个混乱,而不是一个真正的解决方案。

我认为我至少理解了关注分离的概念,但是在理解依赖注入和服务定位器模式方面似乎存在根本错误 - 我不知道那是什么。

我目前了解它的方式 - 这也可能是错误的 - 至少在MVC中,ControllerFactory会为控制器查找构造函数,并调用服务定位器本身以获取所需的依赖关系,然后通过他们进来。但是,我可以理解,并非所有的班级和没有工厂来创建它们。所以在我看来,一些服务定位模式是可以接受的...但是...

  1. 什么时候不可接受?
  2. 当我应该重新思考我如何使用服务定位器模式时,应该注意什么样的模式?
  3. 我的ModelBinder实现是否错误?如果是这样,我需要学习如何解决它?
  4. 在另一个问题中,沿着这一行的用户Mark Seemann推荐了一个抽象工厂 - 这与此有何联系?

我想就是这样 - 我无法真正想到任何其他问题来帮助我理解,但任何额外的信息是非常感谢。

我知道DI可能不是所有事情的答案,我可能会在实现它的过程中过度放弃,但是,它似乎以我期望的方式与单元测试一起工作,而不是如此。

我不是在寻找代码来修复我的示例实现 - 我期待学习,寻找解决方案来解决我有缺陷的理解。

祝希望stackoverflow.com有能力保存草稿问题。我也希望无论谁回答这个问题都能得到适当的回答这个问题的声誉,因为我想我要求很多。提前致谢。

+0

我想你是指马克西曼和这篇博文:http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx – 2011-01-11 12:42:05

回答

14

考虑以下几点:

public class MyClass 
{ 
    IMyInterface _myInterface; 
    IMyOtherInterface _myOtherInterface; 

    public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface) 
    { 
    // Foo 

    _myInterface = myInterface; 
    _myOtherInterface = myOtherInterface; 
    } 
} 

有了这个设计,我能表达我的类型的依赖需求。类型本身并不负责知道如何实例化任何依赖关系,它们是通过使用任何解析机制(通常是IoC容器)给予(注入)的。鉴于:

public class MyClass 
{ 
    IMyInterface _myInterface; 
    IMyOtherInterface _myOtherInterface; 

    public MyClass() 
    { 
    // Bar 

    _myInterface = ServiceLocator.Resolve<IMyInterface>(); 
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>(); 
    } 
} 

我们的类现在依赖创建specfic实例,但通过委派一个服务定位器。在这个意义上,服务位置可以被认为是反模式,因为你没有公开依赖关系,但是你允许通过编译可以捕捉到的问题冒泡到运行时。 (好读是here)。你隐藏了复杂性。

其中一个或另一个之间的选择实际上取决于你的建筑物以及它提供的服务。通常,如果您从头开始构建应用程序,那么我会始终选择DI。它提高了可维护性,增强了模块化,并使测试类型变得更加简单。但是,以ASP.NET MVC3为例,您可以轻松实现SL作为其融入设计的一部分。

您可以随时进行复合设计,您可以在SL中使用IoC/DI,非常类似于使用Common Services Locator。您的组件可以通过DI连线,但通过SL暴露。你甚至可以将组合物混合使用,并使用类似Managed Extensibility Framework(它本身支持DI,但也可以连接到其他IoC容器或服务定位器)的东西。这是一个很大的设计选择,通常我的建议是在可能的情况下针对IoC/DI。

我不会说你的具体设计是错误的。在这种情况下,您的代码不负责创建模型联编程序本身的一个实例,这是由框架决定的,因此您无法控制那个,但可以使用轻松地更改您对服务定位器的使用,以访问IoC容器。 但是在IoC容器上调用解析的动作...你会不会考虑服务位置?

对于抽象工厂模式,工厂专门创建特定类型。您不需要注册类型来解决问题,您基本上注册了一个抽象工厂,并且可以构建您可能需要的任何类型。通过服务定位器,它被设计为定位服务并返回这些实例。类似于公约的观点,但行为却非常不同。

+0

这几乎是我目前使用它。 (公共服务定位器)我的任何类都没有实例化它们自己的依赖关系,只要我可以将它们传递给它们。最后一句是我只是把自己弄糊涂了,因为这是我多次问过自己的同一个问题。我只是讨厌看遍各地的“解决方案”。它只是*似乎*我做错了什么... – Buildstarted 2011-01-05 18:33:06

+0

在这种情况下可能是不可避免的,你想保持一个解耦实现的健壮性,并且你无法控制类型的创建,服务位置是选项... – 2011-01-05 18:37:02