What's the difference between the Dependency Injection and Service Locator patterns?

Viewed 92132

Both patterns seem like an implementation of the principle of inversion of control. That is, that an object should not know how to construct its dependencies.

Dependency Injection (DI) seems to use a constructor or setter to "inject" it's dependencies.

Example of using Constructor Injection:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Service Locator seems to use a "container", which wires up its dependencies and gives foo it's bar.

Example of using a Service Locator:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

Because our dependencies are just objects themselves, these dependencies have dependencies, which have even more dependencies, and so on and so forth. Thus, the Inversion of Control Container (or DI Container) was born. Examples: Castle Windsor, Ninject, Structure Map, Spring, etc.)

But a IOC/DI Container looks exactly like a Service Locator. Is calling it a DI Container a bad name? Is an IOC/DI Container just another type of Service Locator? Is the nuance in the fact that we use DI Containers mostly when we have many Dependencies?

16 Answers

Following simple conception gave me a clearer understanding of difference between Service Locator and DI Container:

  • Service Locator is used in the consumer and it pulls services by ID from some storage by direct consumer's request

  • DI Container is located somewhere outside and it takes services from some storage and pushes them to the consumer (no matter via constructor or via method)

However, we can talk about difference between these only in context of concrete consumer usage. When Service Locator and DI Container are used in composition root, they are almost similar.

Service Locator and Dependency Injection are both Object Access Pattern implementation that obey the Dependency Inversion Principle


Dependency Injection are [static/global] object access pattern

Service Locator are [dynamic] object access pattern


If you need to handle [dynamic structure] like [ui tree] or any [fractal designed application], you may need Service Locator.

Example:

  • createContext/useContext of React
  • provide/inject of Vue
  • Providers of angular

If you only want to get a instance from your class that don't care about the hierarchy of the application and the position of the instance in that hierarchy, you should use DI.

Example:

  • Annotation in C#/Java

Service Locator is used when you don't know the actual provider of the service before runtime.

DI is used when you know it's the static container that provides that service.


Service Locator pattern is more like a module level Dependency Providers, while DI is global level.

It's very useful when there is a sub-module that declare dependency of a service that should be provided by it's parent-module instead of a static resolve type(singleton/transient/static-scoped).

It can be implemented by a scoped injection pattern of DI while the scope is defined by the module structure/relation of the application.


Personal suggestion:

  1. use DI wherever possible.
  2. use Service Locator if you have to deal with dynamic/runtime service resolvation in fractal structure.
  3. encapsulate the Service Locator as a scoped DI, for example: @inject({ scope: 'contextual' })
  4. Locate the service by interface, instead of the class/constructor.

Detailed information: https://docs.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection-guidelines#recommendations

DI container is a superset of service locator. It can be used to locate a service, with additional capability of assembling (wiring) the injections of dependency.

For the record

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Unless you really need an interface (the interface is used by more than one class), you MUST NOT USE IT. In this case, IBar allows utilizing any service class, that implements it. However, usually, this Interface will be used by a single class.

Why it is a bad idea to use an interface?. Because it is really hard to debug.

For example, let's say that the instance "bar" failed, question: which class failed?. Which code I should fix? A simple view, it leads to an Interface, and it's here where my road ends.

Instead, if the code uses a hard dependency then it's easy to debug a mistake.

//Foo Needs an IBar
public class Foo
{
  private BarService bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

If "bar" fails, then I should check and fir the class BarService.

Related