ChatGPT解决这个技术问题 Extra ChatGPT

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

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?

Inversion of control means that "an object should not know how to construct its dependencies"?!? That one is new to me. No, really, that's not what "inversion of control" means. See martinfowler.com/bliki/InversionOfControl.html That article even provides references for the etymology of the term, dating back to the 1980s.
Mark Seemann argues Service Locator as anti-pattern(blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern). Also, I found the diagram (found here, stackoverflow.com/a/9503612/1977871) helpful to understand the DI and SL predicament. Hope this helps.
Thanks will take a look. I remember singletons are anti-pattern and everyone uses them.

u
user2340612

The difference may seem slight, but even with the ServiceLocator, the class is still responsible for creating its dependencies. It just uses the service locator to do it. With DI, the class is given its dependencies. It neither knows, nor cares where they come from. One important result of this is that the DI example is much easier to unit test -- because you can pass it mock implementations of its dependent objects. You could combine the two -- and inject the service locator (or a factory), if you wanted.


Additionally, you can use both when constructing a class. The default constructor can use the SL to retrieve the dependencies, and pass them to the "real" constructor which receives those dependencies. You get the best of both worlds.
No, the ServiceLocator is the one responsible for instantiating the correct implementation for a given dependency (plugin). In the case of DI, the DI "container" is the one responsible for that.
@Rogerio yes but the the class still has to know about the Service Locator... thats two depenencies. Also more often than not I have seen Service Locator delegate to the DI container for lookup particularly for transient objects that need service support.
@Adam I didn't say that the Service Locator would delegate to a DI container. These are two mutually exclusive patterns, as described in the "official" article. To me, Service Locator has a huge advantage over DI in practice: use of a DI container invites abuse (which I've seen repeatedly), while use of a Service Locator does not.
"One important result of this is that the DI example is much easier to unit test -- because you can pass it mock implementations of its dependent objects." Not true. In your unit tests, a call to a register function in the service locator container can be used to easily add mocks to the registry.
J
Joel

When you use a service locator, every class will have a dependency on your service locator. This is not the case with dependency injection. The dependency injector will typically be called only once at startup to inject dependencies into some main class. The classes this main class depends on will recursively have their dependencies injected, until you have a complete object graph.

A good comparison: http://martinfowler.com/articles/injection.html

If your dependency injector looks like a service locator, where the classes call the injector directly, it is probably not a dependency injector, but rather a service locator.


But how do you handle the case where you have to create objects during runtime? If you create them manually with "new" you cannot make use of DI. If you call the DI framework for help, you're breaking the pattern. So what options are left?
@Boris I had the same problem and decided to inject class specific factories. Not pretty but got the job done. Would love to see a prettier solution.
Direct link to comparison: martinfowler.com/articles/…
@Boris If I needed to construct new objects on the fly I would inject an Abstract Factory for said objects. Which would be similar to injecting a service locator in this instance but provides a concrete, uniform, compile-time, interface for building the relevant objects and makes the dependencies explicit.
J
Jeff Sternal

Service locators hide dependencies - you can't tell by looking at an object whether it hits a database or not (for example) when it obtains connections from a locator. With dependency injection (at least constructor injection) the dependencies are explicit.

Moreover, service locators break encapsulation because they provide a global point of access to dependencies of other objects. With service locator, as with any singleton:

it becomes difficult to specify the pre and post conditions for the client object's interface, because the workings of its implementation can be meddled with from outside.

With dependency injection, once an object's dependencies are specified, they are under control of the object itself.


I prefer "Singletons Considered Stupid", steve.yegge.googlepages.com/singleton-considered-stupid
I love ol' Steve Yegge and the title of that article is great, but I think the article I cited and Miško Hevery's "Singletons are Pathological liars" (misko.hevery.com/2008/08/17/singletons-are-pathological-liars) make a better case against the particular devilry of service locator.
This answer is the most correct because it best defines a service locator : "A class that hides its dependencies." Note that creating a dependency internally, while often not a good thing, does not make a class a service locator. Also, taking a dependency on a container is a problem but not "the" problem that most clearly defines a service locator.
With dependency injection (at least constructor injection) the dependencies are explicit.. Please explain.
As above, I cannot see how SL makes dependencies less explicit than DI...
N
Nathan

Martin Fowler states:

With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class – hence the inversion of control.

In short: Service Locator and Dependency Injection are just implementations of Dependency Inversion Principle.

The important principle is “Depend upon Abstractions, not upon Concretions”. This will make your software design “loosely coupled”, “extensible”, “flexible”.

You can use the one that best fits your needs. For a big application, having a huge codebase, you'd better use a Service Locator, because Dependency Injection would require more changes to your codebase.

You can check this post: Dependency Inversion: Service Locator or Dependency Injection

Also the classic: Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler

Designing Reusable Classes by Ralph E. Johnson & Brian Foote

However, the one that opened my eyes was: ASP.NET MVC: Resolve or Inject? That’s the Issue… by Dino Esposito


Fantastic summary: "Service Locator and Dependency Injection are just implementations of Dependency Inversion Principle. "
And he states also : The key difference is that with a Service Locator every user of a service has a dependency to the locator. The locator can hide dependencies to other implementations, but you do need to see the locator. So the decision between locator and injector depends on whether that dependency is a problem.
ServiceLocator and DI have nothing to do with the "Dependency Inversion Principle" (DIP). DIP is a way to make a high-level component more reusable, by replacing a compile-time dependency on a low-level component with a dependency on an abstract type defined together with the high-level component, which gets implemented by the low-level component; in this way, the compile-time dependency gets inverted, as now it is the low-level one which depends on the high-level one. Also, note that Martin Fowler's article explains that DI and IoC are not the same thing.
G
Grant Palin

A class using constructor DI indicates to consuming code that there are dependencies to be satisfied. If the class uses the SL internally to retrieve such dependencies, the consuming code is not aware of the dependencies. This may on the surface seem better, but it is actually helpful to know of any explicit dependencies. It is better from an architectural view. And when doing testing, you have to know whether a class needs certain dependencies, and configure the SL to provide appropriate fake versions of those dependencies. With DI, just pass in the fakes. Not a huge difference, but it is there.

DI and SL can work together, though. It is useful to have a central location for common dependencies (e.g. settings, logger, etc). Given a class using such deps, you can create a "real" constructor that receives the deps, and a default (no parameter) constructor that retrieves from the SL and forwards to the "real" constructor.

EDIT: and, of course, when you use the SL, you are introducing some coupling to that component. Which is ironic, since the idea of such functionality is to encourage abstractions and reduce coupling. The concerns can be balanced, and it depends on how many places you would need to use the SL. If done as suggested above, just in the default class constructor.


Interesting! I'm using both DI and SL, but not with two constructors. Three or four most boring often needed dependencies (settings, etc...) get obtained from the SL on the fly. Everything else gets constructor-injected. It's a bit ugly, but pragmatic.
E
Eliahu Aaron

Both of them are implementation techniques of IoC. There are also other patterns which implements Inversion of Control:

Factory pattern

Service locator

DI (IoC) Container

Dependency injection (constructor injection, parameter injection (if not required), setter injection of interface injection) ...

Service locator and DI Container seem more similar, both of them use a container to define dependencies, which maps abstraction to the concrete implementation.

The main difference is how the dependencies are located, in Service Locator, client code request the dependencies, in DI Container we use a container to create all of objects and it injects dependency as constructor parameters (or properties).


Dependency Injection doesn't require the use of a DI Container though. With DI, a not-that-uncommon approach is to use Pure DI, which basically means you hand-wire your object graphs in the Composition Root.
c
cngzz1

In my last project I use both. I use dependency injection for unit testability. I use service locator to hide implementation and being dependent to my IoC container. And YES! Once you use an IoC container (Unity, Ninject, Windsor Castle), you depend on it. And once it is outdated or for some reason you will want to swap it, you will/may need to change your implementation - at least the composition root. But service locator abstracts that phase.

How would you not depend on your IoC container? Either you will need to wrap it on your own (which is a bad idea), or you use a Service Locator to configure your IoC container. So you will tell the Service Locator to get the interface you need, and it will call the IoC container which is configured to retrieve that interface.

In my case, I use ServiceLocator, which is a framework component. And I use Unity for my IoC container. If in the future I need to swap my IoC container with Ninject, all I need to do is to configure my Service Locator to use Ninject instead of Unity. Easy migration.

Here is a great article that explains this scenario; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/


The johandekoning article link is broken.
Y
Yogesh Umesh Vaity

One reason to add, inspired by a documentation update we wrote for the MEF project last week (I help build MEF).

Once an app is made up of potentially thousands of components, it can be difficult to determine whether any particular component can be instantiated correctly. By "instantiated correctly", I mean that in this example based on the Foo component, an instance of IBar and will be available, and that the component providing it will:

have its required dependencies,

not be involved in any invalid dependency cycles, and

in the case of MEF, be supplied with only one instance.

In the second example you gave, where the constructor goes to the IoC container to retrieve its dependencies, the only way that you can test that an instance of Foo will be able to be instantiated correctly with the actual runtime configuration of your app is to actually construct it.

This has all sorts of awkward side-effects at test time, because code that will work at runtime won't necessarily work under a test harness. Mocks won't do, because the real configuration is the thing we need to test, not some test-time setup.

The root of this problem is the difference already called out by @Jon: injecting dependencies through the constructor is declarative, while the second version uses the imperative Service Locator pattern.

An IoC container, when used carefully, can statically analyze the runtime configuration of your app without actually creating any instances of the components involved. Many popular containers provide some variation of this; Microsoft.Composition, which is the version of MEF targeting .NET 4.5 web and Metro style apps, provides a CompositionAssert sample in the wiki documentation. Using it, you can write code like:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

(See this example).

By verifying the Composition Roots of your application at test time you can potentially catch some errors that may otherwise slip through testing later in the process.

Hope this is an interesting addition to this otherwise comprehensive set of answers on the topic!


N
NoelAdy

I think the two work together.

Dependency injection means you push in some dependant class/interface to a consuming class (usually to it's constructor). This decouples the two classes via an interface and means the consuming class can work with many types of "injected dependency" implementations.

The role of the service locator is to pull together your implementation. You setup a service locator via some boot strapping at the start of your program. Bootstrapping is the process of associating a type of implementation to a particular abstract/interface. Which gets created for you at run time. (based on you config or bootstrap). If you hadn't implemented dependency injection, it would be very difficult to utilise a service locator or IOC container.


E
Eliahu Aaron

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.


Y
Yogesh Umesh Vaity

Note: I'm not exactly answering the question. But I feel that this can be useful for new learners of the Dependency Injection pattern who are confused about it with the Service Locator (anti-)pattern who happen to stumble onto this page.

I know the difference between the Service Locator (it's seems to be regarded as an anti-pattern now) and Dependency Injection patterns and can understand concrete examples each pattern, yet I was confused by examples showing a service locator inside the constructor (assume we're doing constructor injection).

"Service Locator" is often used both as the name of a pattern, and as the name to refer to the object (assume too) used in that pattern to obtain objects without using the new operator. Now, that same type of object can also be used at the composition root to perform dependency injection, and that's where the confusion comes in.

The point to note is that you may be using a service locator object inside a DI constructor, but you are not using the "Service Locator pattern". It is less confusing if one refers it as an IoC container object instead, as you may have guessed that they essentially do the same thing (do correct me if I'm wrong).

Whether it is referred to as a service locator (or just locator), or as an IoC container (or just container) makes no difference as you have guessed, they are probably referring to the same abstraction (do correct me if I'm wrong). It's just that calling it a service locator suggests that one is using the Service Locator anti-pattern together with the Dependency Injection pattern.

IMHO, naming it a 'locator' instead of 'location' or 'locating', can also cause one to sometimes think that the service locator in an article is referring to the Service Locator container, and not the Service Locator (anti-)pattern, especially when there's a related pattern called Dependency Injection and not Dependency Injector.


D
Daniel B

In this oversimplified case there is no difference and they can be used interchangeably. However, real world problems are not as simple. Just assume that the Bar class itself had another dependency named D. In that case your service locator wouldn't be able to resolve that dependency and you would have to instantiate it within the D class; because it is the responsibility of your classes to instantiate their dependencies. It would even get worse if the D class itself had other dependencies and in real-world situations it usually gets even more complicated than that. In such scenarios DI is a better solution than ServiceLocator.


Hmm I'd disagree: the service locator ex. clearly shows that there's still a dependency there... the service locator. If the bar class itself has a dependency, then bar will also have service locator, that's the whole point of using DI/IoC.
u
user5474476

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:

use DI wherever possible. use Service Locator if you have to deal with dynamic/runtime service resolvation in fractal structure. encapsulate the Service Locator as a scoped DI, for example: @inject({ scope: 'contextual' }) 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


Y
Yogesh

What’s the difference (if any) between Dependency Injection and Service Locator? Both patterns are good at implementing the Dependency Inversion principle. The Service Locator pattern is easier to use in an existing codebase as it makes the overall design looser without forcing changes to the public interface. For this same reason, code that is based on the Service Locator pattern is less readable than equivalent code that is based on Dependency Injection.

The Dependency Injection pattern makes it clear since the signature which dependencies a class (or a method) is going to have. For this reason, the resulting code is cleaner and more readable.


G
Glenn Mohammad

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.


m
magallanes

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.


A class is a blueprint to construct specific object. On the other hand an interface is a contract and just defines a behaviour not the action. Instead of passing around actual object, only the interface is shared so that the consumer doesn’t access the rest of your object. Also for unit testing it helps to test only the part that needs to be tested. I guess in time you’d understand its usefulness.