Previously in this series, we have discussed the silly arguments of tabs versus spaces and squash versus no-squash merges. Now we are moving on to constructor injection versus service locator.
Sometimes both sides of an argument are valid, and often you are picking something based off of personal preference. This feels like one of those arguments to me.
Constructor injection and service locator are kinds of Dependency Inversion (DI), which is the D in SOLID (for more on SOLID, check out my entire series). SOLID is an acronym created by Robert Martin, and it is a good concept to be aware of as a software engineer. Writing SOLID software should be a goal.
Now the D in SOLID comes in two flavors, Chocolate and Vanilla. Just kidding. The two flavors are constructor injection and service locator. Both achieve similar goals, but I think they both have different side effects.
With constructor injection, we are injecting other modules in the constructor and then storing them on properties or fields on the module. This is probably the most common form of DI. It is the form supported by Angular out of the box. .NET Core also supports this out of the box. It is very common, and probably the most recommended form of DI. You can see an example of constructor injection below, where the OrderManager gets its dependencies through its constructor.
Service locator achieves similar goals, but does so with a different style. Service Locator uses a factory to create instances of a module. Many consider this an anti-pattern, but I personally prefer it. .NET Core also supports this out of the box. You can see an example of service locator below. Below the OrderManager gets its dependencies using a factory.
Both constructor injection and service locator are great patterns for ensuring we are not programming against concrete implementations of services (modules). I prefer service locator because I believe it hides more information. A constructor injection person would argue that constructor injection makes dependencies more obvious. Both arguments are valid; we are in an area of taste. Use one, stick with it, be happy.
Links to Larger Examples
Larger constructor injection example:
Larger service locator example: