为什么Unity使用服务定位器?

7

我在几个使用Unity的asp.net mvc3教程中看到了这行代码。我认为服务定位器是一种反模式,不是最佳实践。这个服务定位器与定义的反模式不同吗?还是这行代码/实现被认为是不好的做法。

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(Container));

1
我也经常看到这种用法,在Prism StockTrader RI中,他们也使用了MEF的ServiceLocator。我之前认为这是一种反模式,所以看到RI中还有它真的很惊讶。我确信这是与反模式中定义的Service Locator模式实现相同的。 - Lukazoid
1
你搞反了:在你的例子中,不是Unity在使用服务定位器,而是你的代码插件通过服务定位器将Unity插入到asp.net mvc3中。关于服务定位器模式的争论是宗教性质的。asp.net mvc团队必须提供一种使用您喜欢的DI容器的方法,这就是他们实现的方式。考虑替代方案。这里有更多关于这个问题的见解http://blog.ploeh.dk/2011/08/25/ServiceLocatorRolesVsMechanics.aspx - Andrew Savinykh
1
@zespri - 所以并不是所有的Unity实现都使用服务定位器吗? - Travis J
我所知道的只有一种Unity实现,它不使用服务定位器。如果你愿意,你可以将其用作服务定位器(即使Unity没有明确支持它),但它确实支持这种情况。 - Andrew Savinykh
1
@zespri - 我非常希望避免使用服务定位器,你知道有没有任何教程适用于 ASP.NET MVC3,展示一个简单的 Unity 设置,而不需要服务定位器? - Travis J
请看这里,了解DI如何与asp.net连接的精彩内部实现。http://bradwilson.typepad.com/blog/2010/07/service-location-pt1-introduction.html - Andrew Savinykh
3个回答

13

虽然这个问题比较老,但是为了让其他人受益:

虽然我绝对同意 "服务定位器是一种反模式" 的口号,但也有例外。

当你使用依赖注入(如Unity)时,确实不要使用ServiceLocator,而是只对所有服务类使用构造函数注入。(还不要对值对象(如DTO)使用“new”)

但是,在某些情况下,你简单地无法使用构造函数注入,而唯一的获取某个服务的方法是使用特殊处理来直接访问Unity容器,在这种情况下,ServiceLocator是实现这一目标的良好标准方式。例如,当该类不是由你(或更具体地说,不是由Unity)实例化,而是由.NET框架实例化时。

一些ServiceLocator可能有用的简单示例包括从以下位置获取已注册在您的Unity容器中的服务:

  1. WCF IEndpointBehavior或IClientMessageInspector的实现
  2. WPF IValueConverter的实现
  3. 或者你可能甚至不想从类中获取“服务”,而只是想编写可进行单元测试的代码,但由于该类通常由.NET Framework构建,所以无法轻松实例化它(或者根本不能实例化),因此你将自定义代码提取到可测试的类中,并在不可测试的类中使用ServiceLocator解析。

请注意,这行代码并非理想:

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(Container));

每次访问ServiceLocator.Current属性时,都会执行提供的委托,也就是说,每次都会创建一个新的UnityServiceLocator对象。相反,你可能想要这样实现:

IServiceLocator locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);

7
如果您创建一个旨在与容器无关的框架,则服务定位器(虽然它不应该出现在应用程序中)是一层额外的间接层,使您可以将Unity切换为其他框架。此外,使用服务定位器并不强制要求使用DI来构建使用该框架的应用程序。

2
这是人们谈论的同一反模式。所有该行所做的就是将服务定位器提供程序设置为UnityServiceLocator的实例,即使用ISerivceLocator的Unity实现。如果您愿意,您可以拥有自己的IServiceLocator实现,并使用它来代替UnityServiceLocator。
出于各种原因,使用服务定位器被认为是一种不好的做法,如此处所列。

我同意服务定位器是一种不好的实践,只是我不确定这是否仅仅是名称上的使用。 - Travis J
3
如果您正在使用一个IOC容器,如何找到该容器? - phil soady
它有其用处,参见另一个答案,它使您的代码不会直接绑定到特定的 DI 实现。 - Alwyn
@philsoady 我认为对于桌面应用程序,解决方案是向需要容器的任何类添加类型为IUnityContainer的构造函数参数(请参见http://mvvmandunity.codeplex.com/)。对于其他应用程序类型,可能无法这样做,因此需要使用服务定位器。个人而言,如果没有其他选择,仅使用它来获取DI容器是可以接受的。 - Stephen Hewlett

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接