ServiceLocator 实现

当我第一次看到ServiceLocator这个名字的时候以为只是一个常见的类名而已,后来才知道这其实是一种设计模式。ServiceLocator 充当了一个中介的角色,负责定位和提供服务的实例。它最终目的是为了解决依赖管理的问题。
ServiceLocator 实现一个简单的例子:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657public interface IService{ void Execute();}public class ServiceA : IService{ public void Execute() { Console.WriteLine("Service A is executing."); }}public class ServiceB : IService{ public void Execute() { Console.WriteLine("Service B is executing."); }}public class ServiceLocator{ private static Dictionary
ServiceLocator 就做了一件事:负责提供服务的实例。
为什么需要 ServiceLocator?当我们还是小白,没有任何设计模式的概念时,代码应该是这样写的:
1234567891011121314151617181920public class ServiceA{ public void Execute() { Console.WriteLine("Service A is executing."); }}public class ServiceB{ public void Execute() { var serviceA = new ServiceA(); serviceA.Execute(); Console.WriteLine("Service B is executing."); }}var serviceA = new ServiceB();serviceA.Execute();
显然,ServiceA 依赖于 ServiceB 的实现,这违反了依赖倒置原则。于是我们引入了接口:
123456789101112131415161718192021222324public interface IServiceA{ void Execute();}public class ServiceA : IServiceA{ public void Execute() { Console.WriteLine("Service A is executing."); }}public class ServiceB{ public void Execute(IServiceA serviceA) { serviceA.Execute(); Console.WriteLine("Service B is executing."); }}var serviceB = new ServiceB();serviceB.Execute(new ServiceA()); // 注入依赖参数,术语称为 "依赖注入"
现在符合了依赖倒置原则,即:抽象不应该依赖于细节,细节应该依赖于抽象。为什么需要服务定位器?当我们频繁使用依赖注入时,实际上在注入上下文中也产生了依赖:
12345678public class Foo{ public void Bar() { var serviceB = new ServiceB(); serviceB.Execute(new ServiceA()); }}
此时虽然 ServiceB 不依赖 ServiceA,但是 Foo 却和 ServiceA、ServiceB 耦合了。此时就需要 ServiceLocator 登场了。通过使用 ServiceLocator,应用程序的各个部分可以统一通过服务定位器获取它们所需的服务,而无需直接依赖于具体的实现类。这有助于减少类之间的直接依赖,提高灵活性和可维护性。采用 ServiceLocator 后的代码:
12345678public class Foo{ public void Bar() { var serviceB = ServiceLocator.GetService
Common Service LocatorCommonServiceLocator 是一个服务定位器的通用实现。相较于普通的服务定位器,CommonServiceLocator的区别在于它是全局唯一的服务定位器,通过设置全局的 ServiceLocator 提供者,可以在整个应用程序中使用同一个 ServiceLocator 来解析服务,而不需要每个地方都创建一个新的服务定位器实例。以 Unity 容器为例:
1234567891011using CommonServiceLocator;using Unity;using Unity.ServiceLocation;var container = new UnityContainer();container.RegisterType
服务定位器会隐藏具体的容器,使得调用者依赖定位器而不是容器。除了 Unity 外,一些主流的容器都有对应的 CommonServiceLocator 包装。
参考Service Locator Pattern in C#Common Service LocatorService Locator is an Anti-PatternDependency Injection vs Service LocationWhat’s the difference between the Dependency Injection and Service Locator patterns?