[Architecture Pattern] Service Locator
动机
Service Locator是一个在开发系统时,很常用的一个模式。在Martin Fowler写的Inversion of Control Containers and the Dependency Injection pattern里,可以发现这个Pattern的身影。Service Locator最主要是定义BLL层内对象生成、对象存放、对象取得的职责,让系统在取得对象时不需要知道对象是如何生成及存放,有效降低系统的耦合性。
同时学习Service Locator,也为架构设计带入了空间的概念。在设计架构的时候,可以套用Service Locator来做为架构空间的封装。将经对象生成建立的对象,「存放」在Service Locator内,让目标架构「存在」一组对象可以提供使用。
本篇文章介绍一个Service Locator的实做,这个实做定义对象之间的职责跟互动,用来完成Service Locator应该提供的功能及职责。为自己做个纪录,也希望能帮助到有需要的开发人员。
结构
接下来沿用[Architecture Pattern] Repository建立的UserCountService,来当作范例的内容。另外在BLL层使用Service Locator来封装UserCountService的对象生成、对象存放、对象取得。范例的结构如下:
主要的参与者有:
ServiceLocator
-提供存放对象的功能给系统使用。
-提供存放的对象给系统使用。
Client
-使用ServiceLocator内存放的UserCountService。
UserCountService
-使用系统内User数据计算各种Count。
-由Client生成或是ServiceLocator生成。
透过下面的图片说明,可以了解相关对象之间的互动流程。
实做
Service Locator由两种运作逻辑所组成:定位逻辑、生成逻辑。定位逻辑是整个Service Locator的核心,它定义对象存放、对象取得的职责。而对象必须要被生成才能够使用,生成逻辑就是定义对象生成的职责。接着透过实做一组Service Locator,解析Service Locator内的运作逻辑,帮助开发人员理解Service Locator模式。
范列下载
实做说明请参照范例程序内容:ServiceLocatorSample点此下载
定位逻辑
首先建立ServiceLocatorSample.BLL项目,并且建立ServiceLocator对象用来封装Service Locator的定位逻辑:ServiceLocator提供SetInstance方法,让系统可以存放各种型别的对象。并且ServiceLocator提供GetInstance方法,让系统可以取得先前存放的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public partial class ServiceLocator { // Fields private readonly Dictionary<Type, object > _serviceDictionary = new Dictionary<Type, object >(); // Methods public TService GetInstance<TService>() where TService : class { // Result TService service = default (TService); // Exist if (_serviceDictionary.ContainsKey( typeof (TService)) == true ) { service = _serviceDictionary[ typeof (TService)] as TService; } // Return return service; } public void SetInstance<TService>(TService service) where TService : class { #region Require if (service == null ) throw new ArgumentNullException(); #endregion // Set if (_serviceDictionary.ContainsKey( typeof (TService)) == false ) { _serviceDictionary.Add( typeof (TService), service); } else { _serviceDictionary[ typeof (TService)] = service; } } } |
另外ServiceLocator也套用了Singleton pattern,让系统能方便的使用ServiceLocator。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public partial class ServiceLocator { // Singleton private static ServiceLocator _current; public static ServiceLocator Current { get { if (_current == null ) { _current = new ServiceLocator(); } return _current; } set { _current = value; } } } |
外部生成逻辑
再来建立一个Console项目,透过ServiceLocator取得UserCountService用来打印的人员数量。而UserCountService是由Console专案来生成、注入ServiceLocator。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Program { static void Main( string [] args) { // Initialize InitializeServiceLocator(); // UserCountService UserCountService userCountService = ServiceLocator.Current.GetInstance<UserCountService>(); // Print Console.WriteLine( "All Count : " + userCountService.GetAllCount()); Console.WriteLine( "Men Count : " + userCountService.GetMenCount()); // End Console.ReadLine(); } static void InitializeServiceLocator() { // UserRepository IUserRepositoryProvider userRepositoryProvider = new SqlUserRepositoryProvider(); UserRepository userRepository = new UserRepository(userRepositoryProvider); UserCountService userCountService = new UserCountService(userRepository); // SetInstance ServiceLocator.Current.SetInstance<UserCountService>(userCountService); } } |
内部生成逻辑
到目前为止范例程序,已经可以透过ServiceLocator取得UserCountService用来打印的人员数量。但UserCountService是由ServiceLocator之外的函式来生成、存放至ServiceLocator。这造成每次重用的时候,必须要重新建立对象生成、存放的功能。
为了增加ServiceLocator的重用性,所以修改ServiceLocator对象,封装Service Locator的生成逻辑:ServiceLocator提供CreateInstance方法,让系统可以建立各种型别的对象。并且变更GetInstance的运作流程,让系统取不到先前存放的对象时,会去使用CreateInstance来生成对象。
(因为是仿真范例,简化了很多UserCountService的设计,并且采用直接建立的方式来示意。实际项目可以采用各种IoC Framework来做生成注入,或是套用各种Factory pattern,这些都能提高ServiceLocator的重用性。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | public partial class ServiceLocator { // Fields private readonly Dictionary<Type, object > _serviceDictionary = new Dictionary<Type, object >(); // Methods protected virtual TService CreateInstance<TService>() where TService : class { // Result TService service = default (TService); // UserCountService if ( typeof (TService) == typeof (UserCountService)) { IUserRepositoryProvider userRepositoryProvider = new CsvUserRepositoryProvider(); UserRepository userRepository = new UserRepository(userRepositoryProvider); UserCountService userCountService = new UserCountService(userRepository); service = userCountService as TService; } // Return return service; } public TService GetInstance<TService>() where TService : class { // Result TService service = default (TService); // Exist if (_serviceDictionary.ContainsKey( typeof (TService)) == true ) { service = _serviceDictionary[ typeof (TService)] as TService; } if (service != null ) return service; // Create service = this .CreateInstance<TService>(); if (service != null ) this .SetInstance<TService>(service); // Return return service; } public void SetInstance<TService>(TService service) where TService : class { #region Require if (service == null ) throw new ArgumentNullException(); #endregion // Set if (_serviceDictionary.ContainsKey( typeof (TService)) == false ) { _serviceDictionary.Add( typeof (TService), service); } else { _serviceDictionary[ typeof (TService)] = service; } } } |
最后修改Console项目,移除项目内生成UserCountService的逻辑。并且同样透过ServiceLocator取得UserCountService用来打印的人员数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Program { static void Main( string [] args) { // UserCountService UserCountService userCountService = ServiceLocator.Current.GetInstance<UserCountService>(); // Print Console.WriteLine( "All Count : " + userCountService.GetAllCount()); Console.WriteLine( "Men Count : " + userCountService.GetMenCount()); // End Console.ReadLine(); } } |
后记
整个Service Locator实做看来下,眼尖的开发人员会发现,它跟IoC Framework有异曲同工的意味。Service Locato跟IoC Framework的差异,主要是在:IoC Framework的主要职责是对象生成,Service Locator的主要职责是对象存放、对象取得。只是发展到了后来,两者几乎都实做了对象生成、对象存放、对象取得三个职责。换个方式说,大多的IoC Framework都封装了Service Locator的职责。部分Service Locator也封装了IoC Framework的职责。虽然说结果看起来是一样,但两者设计的出发点是有差异的。
而Service Locator有很多实做版本,这些实做版本依照需求分割、设计,BLL层内对象生成、对象存放、对象取得的职责,用以提高整体架构的重用性、可维护性。一个系统的成败,除了最基本的满足客户需求之外,这些额外的非功能需求也是很重要的一环。这让后续接手维护的开发人员,能够早点回家吃晚餐。:D
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?