When a system component needs to call a service to complete a specific task, the simplest way is to use the new keyword to create an instance of the service, you can also decouple the specific implementation part of the component and service in the factory mode to obtain the instance of the service in a more flexible way, such as configuration information. However, these practices have their respective drawbacks:
- Directly maintaining the reference to the service instance in the component will cause the dependency between the component and the service. When you need to replace the specific implementation of the service, you have to modify the part of the component that calls the service and re-compile the solution. Even if you use the factory mode to dynamically obtain the service instance based on the configuration information, cannot provide components with a central location for managing service instances for different service types.
- This dependency between components and services restricts the project development process. In actual projects, the development process is usually parallel, but not completely synchronous. For example, the development of components is carried out simultaneously with the development of services required by them, however, it is very likely that when the component needs to call the service, the service has not completed development and standalone testing. When this problem occurs, the component call service is usually temporarily vacant. After the service completes development and standalone testing, it is integrated into the component code. However, this method is not only time-consuming, but also increases the risk of errors.
- Standalone testing for components becomes complicated. Every time you perform a standalone test on a component, You have to configure and run the required service for the component. Instead, you cannot use service stub to solve the dependency between the component and the service.
- A service instance may need to be referenced in multiple components. In this case, the code for directly creating a service instance is distributed throughout the program, resulting in multiple copies of a program, greatly increase maintenance and troubleshooting costs
- When a component needs to call multiple services, different services may initialize their instances in different ways. Developers have to understand the APIS initialized by all services so that they can be correctly used in programs.
- The initialization process of some services consumes a lot of resources. Therefore, repeated service initialization will greatly increase the resource usage and performance consumption of the system. The program requires a mechanism to manage the service initialization process. While initializing interfaces uniformly, it also needs to provide some caching functions for the program.
To solve the above problems, we can introduceService Locator)Enterprise application architecture model.
Service locator Mode
The service locator mode is an enterprise-level application architecture that provides a central location for service creation and initialization in applications, and solved the various design and development problems mentioned above. The service locator model mainly includes the following participants:
Service)
A service is a specific instance that the service locator needs to return to the caller. For example, a service locator can return a service that outputs information to the console or a service that outputs information to the file system based on the caller's needs. In this case, the two services may have different interfaces: for a service that outputs information to the console, it only needs to receive one parameter (that is, the information to be output) the output task can be completed. For the service that outputs information to the file system, it not only obtains the information to be output, but also obtains a correct file name, to output information to this file.
Therefore, in practice, we usually design different interfaces for different service types, and the service locator should return the corresponding service instance according to the service type specified by the caller.
Service Factory)
A service factory is an implementation of the factory model. It is responsible for creating and initializing certain types of services. For example, a service that outputs information to the console is created and initialized by a factory, while a service that outputs information to the file system is created by another factory. It can be seen that different service types have specific service factories. In actual applications, service factories correspond to a specific service interface.
Using the Service Factory not only decouples the definition and specific implementation of the service, but also changes different implementation methods of the service without re-compiling the application. For services that consume a large amount of resources during initialization, the service factory can also provide cache functions to improve application performance.
Initial Context
Because different services need to be created and initialized by different service factories, a specific manager is required for service locators to manage these service factories in a unified manner, and initialcontext serves as the role. When you call a service locator to request a service instance, the service locator obtains the Service Factory instance through initialcontext, and then creates the service instance by the Service Factory and returns it to the caller. The advantage of initialcontext is that it simplifies the role of the service locator and provides a powerful guarantee for the management and cache of the Service Factory.
Service Positioner
The service locator provides a central location for the caller to obtain the desired service instance.
Implementation
Assume that two types of services exist in a system. One service outputs the information transmitted by the caller to the console, which is called the console output service; another service outputs the information transmitted by the caller to the file system, which is called the file system output service. The analysis shows that the interfaces of these two services are different: the console output service only needs to obtain the information to be output, and the file system output service also needs to obtain a file name, to output information to the specified file. Therefore, it is obvious to design two interface definitions for these two different services. Furthermore, in order to hide the service initialization process and provide a certain caching mechanism, we will also design a service factory for each service type (or each service interface, its responsibilities have been mentioned above, so I will not repeat them here. In actual projects, we can provide a servicefactory abstract base class for the Service Factory. This aims to unify the mechanisms related to caching in the base class, subclass is directly responsible for the creation and initialization of service instances.
Next, the implementation of initial context is relatively simple. You only need to maintain the service factory for different service types. I don't think the implementation of initial context is necessary either. For some simple application scenarios, it can be completely replaced by service locator. However, in the initial context design, service locator needs to transfer the work of obtaining the service instance to initial context for execution. In fact, IOC containers is the implementation of service locator, but they are often complicated, including complex functions such as loop reference parsing and lifecycle management, with limited time, I have not studied the IOC framework in depth, and I am not planning to discuss it further.
According to the above analysis, service locator has the following structure:
You can use the following UML sequence diagram to describe the execution process:
Yes 【Click here] Download the source code for the above implementation (thinking: please change the service locator in the code to a generic implementation ).
Implementation in byteart retail case
In the byteart retail case, the implementation of the service locator mode has been "degraded" to encapsulate the existing IOC container (Enterprise Library unity). The purpose of encapsulation is to simplify the development process, to provide a central location for applications to access the IOC container. In addition, this encapsulation decouples the application's dependency on the IOC container. For example, in the source code of the byteart retail case, the IOC container is not directly referenced, but the agent is performed through the servicelocator class, this provides convenience for the future implementation of service positioner replacement (of course, this component will not be replaced in general ).
For the servicelocator class code, seeServicelocatorClass.
References
- Service locator pattern (Wikipedia): http://en.wikipedia.org/wiki/Service_locator_pattern
- Service locator pattern (core J2EE patterns): http://www.oracle.com/technetwork/java/servicelocator-137181.html
- Service locator pattern (msdn): http://msdn.microsoft.com/en-us/library/ff648968.aspx
- Dependency injection: http://msdn.microsoft.com/en-us/library/dd458879.aspx
- Inversion of control: http://msdn.microsoft.com/en-us/library/dd458907.aspx