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

試著忘記壹切 提交于 2019-12-16 22:34:42

问题


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?


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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




回答5:


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.




回答6:


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

  • factory pattern
  • service locator
  • dependency injection (constructor injection, parameter injection (if not required), setter injection of interface injection) ...

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

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




回答7:


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 one of IoC containers (Unity, Ninject, Windsor Castle) you depend on it. And once it is outdated or for some reason if you will want to swap it, you will/may need to change your implementation - at least composition root. But service locator abstracts that phase.

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

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

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




回答8:


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.




回答9:


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 determined 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!




回答10:


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 is 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 guess as 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.




回答11:


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.




回答12:


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.




回答13:


Following simple conception gave me a clearer understanding of difference between SL and DI (I'm just remember a words in bold):

SL used in the consumer and it is pulls services by ID from some storage by direct consumer's request; DI 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 SL and DI are used in composition root, they are almost similar.




回答14:


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.



来源:https://stackoverflow.com/questions/1557781/whats-the-difference-between-the-dependency-injection-and-service-locator-patte

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!