Problem
You have classes that have dependencies on services whose concrete type is specified at design time. In this example, ClassA has dependencies on ServiceA and ServiceB. Figure 1 illustrates this.
Figure 1
ClassA has dependencies on ServiceA and ServiceB
This situation has the following constraints:
- To replace or update the dependencies, you must change your class source code.
- The concrete implementation of the dependencies must be available at compile time.
- Your classes are difficult to test in isolation because they have a direct reference to their dependencies and therefore the dependencies cannot be replaced with stubs or mocks.
- Your classes contain repetitive code for creating, locating, and managing their dependencies.
Forces
- You want to decouple your classes from their dependencies so that these dependencies can be replaced or updated with minimal or no changes to your classes’ source code.
- You want to write classes that depend on classes whose concrete implementation is not known at compile time.
- You want to be able to test your classes in isolation, without using the dependencies.
- You want to decouple your classes from being responsible for locating and managing the lifetime of dependencies.
Solution
Create a service locator that contains references to the services and that encapsulates the logic to locate them. In your classes, use the service locator to obtain service instances. Figure 2 illustrates this.
Figure 2
ClassA uses the service locator to get an instance of ServiceA
The service locator does not instantiate the services. It provides a way to register services and it holds references to the services. After the service is registered, the service locator can find the service.
Note: |
| The service locator should provide a way to locate a service without specifying the concrete type. For example, it could use a string key or a service interface type. |
Implementation Notes
The Service Locator pattern can be implemented in several ways. For example, the service locator can be a singleton global instance that holds references to services.
In the Composite Web Application Block, composition containers contain a Services collection to hold references to services. Modules use this collection to register and locate services. For more information about how services are located in the Composite Web Application Block, see the section “Locating Services” in the Services topic.
Liabilities
The Service Locator pattern has the following liabilities
- There are more solution elements to manage.
- You have to write additional code to add service references to the locator before your objects use it.
- Your classes have an extra dependency on the service locator.
- The source code has added complexity; this makes the source code more difficult to understand.
Related Patterns
- Dependency Injection. The Dependency Injection pattern solves the same problems that the Service Locator pattern solves, but it uses a different approach.
- Inversion of Control. The Service Locator pattern is a specialized version of the Inversion of Control pattern.