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): }