Motivation
Service locator is a common mode during system development. The pattern can be found in the inversion of control containers and the dependency injection pattern written by Martin Fowler. Service locator defines the responsibility of generating, storing, and obtaining objects in the BLL layer, so that the system does not need to know how to generate and store objects when obtaining objects, effectively reduces system coupling.
At the same time, learning service locator brings the concept of space into the architecture design. When designing the architecture, you can use service locator as the encapsulation of the architecture space. Create an object generated by the object and store it in the service locator so that the target architecture "exists" and a group of objects can be used.
This article introduces the implementation of a service locator, which defines the responsibilities and interactions between objects to complete the functions and responsibilities that the service locator should provide. Make a record for yourself and hope to help developers who need it.
Structure
The usercountservice created by [Architecture Pattern] repository is used as an example. In addition, The bll layer uses service locator to encapsulate the generation, storage, and retrieval of usercountservice objects. The sample structure is as follows:
Major participants include:
Servicelocator
-Provides the object storage function for the system.
-Provides storage objects for the system.
Client
-Use the usercountservice in servicelocator memory.
Usercountservice
-Use user data in the system to calculate various counts.
-Generated by the client or servicelocator.
The following figure shows the interaction process between objects.
Practice
Service locator is composed of two types of operation logic: positioning logic and generation logic. The positioning logic is the core of the entire service locator. It defines the responsibilities of Object Storage and object acquisition. Objects must be generated before they can be used. The generation logic defines the responsibility of object generation. Then, a set of service locator is implemented to parse the operation logic in the service locator and help developers understand the service locator mode.
Model column download
For more information, see servicelocatorsample.
Positioning Logic
First, create the servicelocatorsample. BLL project, and create a servicelocator object to encapsulate the positioning logic of service locator: servicelocator provides the setinstance method, allowing the system to store various types of objects. In addition, servicelocator provides the getinstance method, allowing the system to retrieve previously stored objects.
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; } }}
In addition, servicelocator also applies Singleton pattern, enabling the system to conveniently use servicelocator.
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; } }}
External generation logic
Create a console project and use servicelocator to obtain the number of persons that usercountservice uses to print. Usercountservice is generated and injected into servicelocator by the console project.
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); }}
Internal generation logic
So far, the sample program can use servicelocator to obtain the number of persons that usercountservice uses to print. However, usercountservice is generated and stored in servicelocator by a function other than servicelocator. This makes it necessary to re-create the object generation and storage functions for each reuse.
To increase the reusability of servicelocator, modify the servicelocator object and encapsulate the generation logic of service locator: servicelocator provides the createinstance method, allowing the system to create various types of objects. In addition, when the system fails to retrieve the previously stored objects, it will use createinstance to generate the objects.
(Because it is a simulation example, it simplifies the design of many usercountservices, and uses a direct establishment method. The actual project can use various IOC frameworks to generate injection, or apply various factory pattern, which can improve the reusability of servicelocator .)
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; } }}
Finally, modify the console project and remove the logic for generating usercountservice in the project. In addition, servicelocator is used to obtain the number of persons used for printing usercountservice.
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(); }}
Postscript
In practice, the entire service locator can be found by eye-catching developers that it is similar to IOC framework. The difference between service locato and IOC framework is mainly in: IOC framework is mainly responsible for object generation, and service locator is mainly responsible for Object Storage and object acquisition. After the development, almost both of them have three responsibilities: Object generation, object storage, and object acquisition. In another way, most IOC frameworks encapsulate the duties of service locator. Some service locator packages the responsibilities of IOC framework. Although the results look the same, the design of the two is different.
There are many actual service locator versions. These actual versions are divided and designed according to requirements. The responsibilities of object generation, object storage, and object acquisition in The bll layer are used to improve the reusability and maintainability of the overall architecture. In addition to meeting the customer's needs, the success or failure of a system is also an important part of these additional non-functional requirements. This allows developers who take over maintenance to go home for dinner early. : D