Поделиться через


Dependency Injection

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

Problem

You have classes that have dependencies on services or components whose concrete type is specified at design time. In this example, ClassA has dependencies on ServiceA and ServiceB. Figure 1 illustrates this.

Ff921152.4722b85b-ac16-4e68-b46d-c70e52adcd72(en-us,PandP.10).png

Figure 1
ClassA has dependencies on ServiceA and ServiceB

This situation has the following constraints:

  • To replace or update the dependencies, you must change your classes' source code.
  • The concrete implementation of the dependencies must be available at compile time.
  • Your classes are difficult to test in isolation because they have a direct reference to their dependencies. This means that these dependencies cannot be replaced with stubs or mocks.
  • Your classes contain repetitive code for creating, locating, and managing their dependencies.

Forces

Any of the following conditions justifies using the solution described in this pattern:

  • You want to decouple your classes from their dependencies so that these dependencies can be replaced or updated with minimal or no changes to your classes' source code.
  • You want to be able to write classes that depend on classes whose concrete implementation is not known at compile time.
  • You want to be able to test your classes in isolation, without using the dependencies.
  • You want to decouple your classes from being responsible for locating and managing the lifetime of dependencies.

Solution

Do not instantiate the dependencies explicitly in your class. Instead, declaratively express dependencies in your class definition. Use a Builder object to obtain valid instances of your object's dependencies and pass them to your object during the object's creation and/or initialization. Figure 2 illustrates this.

Ff921152.360cdd41-4418-4fff-9dee-fc6140458c1d(en-us,PandP.10).png

Figure 2
Conceptual view of the Dependency Injection pattern

Note

Typically, you express dependencies on interfaces instead of concrete classes. This enables easy replacement of the dependency concrete implementation without modifying your classes' source code.

The following are the two main forms of dependency injection:

  • Constructor injection
  • Setter injection

In constructor injection, you use parameters of the object's constructor method to express dependencies and to have the builder inject it with its dependencies. In setter injection, the dependencies are expressed through setter properties that the builder uses to pass the dependencies to it during object initialization.

Implementation Using the Unity Application Block

The Dependency Injection pattern can be implemented in several ways. The Unity Application Block (Unity) provides a container that can be used for dependency injection. For more information about the Unity Application Block, see Unity Application Block.

Example

The NewsReaderPresenter class of the Stock Trader Reference Implementation (located at StockTraderRI.Modules.News\Article\NewsReaderPresenter.cs) uses constructor dependency injection to obtain a valid instance of a view that implements the INewsReaderView interface. The class definition is shown in the following code.

public class NewsReaderPresenter : INewsReaderPresenter
{
    private INewsReaderView readerView;

    public NewsReaderPresenter(INewsReaderView view)
    {
        this.readerView = view;
    }

    public void SetNewsArticle(NewsArticle article)
    {
        readerView.Model = article;
    }

    public void Show()
    {
        readerView.ShowView();
    }
}

Because the NewsReaderPresenter class uses dependency injection to obtain its dependencies, its dependencies can be replaced with mock implementations when testing. The following test methods, taken from the fixture file StockTraderRI.Modules.News.Tests\NewsView\NewsReaderPresenterFixture.cs, show how the NewsReaderPresenter class can be tested in isolation using mock implementations to replace the classes' dependencies and verify behavior.

[TestMethod]
public void ShowInformsViewToShow()
{
    var view = new MockNewsReaderView();
    var presenter = new NewsReaderPresenter(view);

    presenter.Show();

    Assert.IsTrue(view.ShowViewWasCalled);
}

[TestMethod]
public void SetNewsArticlesSetsViewModel()
{
    var view = new MockNewsReaderView();
    var presenter = new NewsReaderPresenter(view);

    NewsArticle article = new NewsArticle() { Title = "My Title", Body = "My Body" };
    presenter.SetNewsArticle(article);

    Assert.AreSame(article,view.Model);
}

Liabilities

The dependency injection pattern has the following liabilities:

  • There are more solution elements to manage.
  • You have to ensure that, before initializing an object, the dependency injection framework can resolve the dependencies that are required by the object.
  • There is added complexity to the source code; therefore, it is harder to understand.

Related Patterns

The following patterns are related to the Dependency Injection pattern:

  • Inversion of Control. The Dependency Injection pattern is a specialized version of the Inversion of Control pattern where the concern being inverted is the process of obtaining the needed dependency.
  • Service Locator. The Service Locator pattern solves the same problems that the Dependency Injection pattern solves, but it uses a different approach.

More Information

For more information on the Dependency Injection pattern, see the following:

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.