Service locators allow you to remove required data from an interface of an object and instead get these data from a secondary object - the service locator.
We normally have direct dependency declarations in APIs:
class A(data1: B, data2: C)
{
}
Compare this to using a service locator:
class A(locator: L)
{
data1 = locator.locate_data1();
data2 = locator.locate_data2();
}
Where code internal to the A acquires B and C from the locator.
Drawbacks
The biggest drawback with a service locator is that we now make dependencies implicit. Depending on whether your locator default-constructs locatables on non-existence or not, your program can also start failing dynamically instead of statically.Another drawback is the potential creation of cycles. In a language without a garbage collector this can lead to memory leaks.
Advantages
This pattern is useful if we need provide dependencies to a heterogeneous set of classes all with their own peculiar dependencies, while these classes are all created in a uniform way. An example is providing input data to a set of heterogeneous types.
let v: Collection<dyn SomeInterface> = ...;
let locator = ...;
for a in v {
v.use_locator(locator):
}