The Dependency Injection Pattern

Embed Size (px)

Citation preview

  • 8/3/2019 The Dependency Injection Pattern

    1/10

    The Dependency Injection Pattern What is it and why do I care?

    A couple of weeks ago I wrote about using the Inversion of Control (IoC) principle to create classes that

    are easier to unit test. One major thing I left out of that post was using the Dependency Injection pattern

    to loosely couple classes from their dependencies. In a nutshell, dependency injection just means that a

    given class or system is no longer responsible for instantiating their own dependencies. In this case

    Inversion of Control refers to moving the responsibility for locating and attaching dependency objects to

    another class or a DI tool. That might not sound that terribly profound, but it opens the door for a lot of

    interesting scenarios.

    While theres a fair amount of unnecessary buzz and hype about the concept, Ive found Dependency

    Injection to be very advantageous for doing Test Driven Development without pulling your hair out. If

    youve seen articles or blog posts about Dependency Injection but dont quite internalize the value of DI

    yet, here are the facts as I see them:

    1. Dependency Injection is an important pattern for creating classes that are easier to unit test in

    isolation

    2. Promotes loose coupling between classes and subsystems

    3. Adds potential flexibility to a codebase for future changes

    4. Can enable better code reuse

    5. The implementation is simple and does *not* require a fancy DI tool

    ThePicoContainer team even has silly tee shirts printed up that say I expected a paradigm shift, but all I

    got was a lousy constructor function. DI is certainly a case where a minimum of effort supplies quite a

    bit of benefit. Dont blow it off just because it seems trivial.

    There are tools out there that do Dependency Injection in .Net. I use my own tool calledStructureMap in

    my development, but Im going to focus on only the concept of DI in this post.

    Example Problem

    My first experience with conscious usage of DI was a WinForms client communicating with the backend via

    web services that was built with theModel View Presenter (Humble Dialog Box) architecture for easier

    unit testing. Most screens in the client end up with something like this set of classes:

    Model Whatever business object/DataSet/chunk of data is being displayed or edited

    View A WinForms UserControl class. Displays data to a user and captures user input and screen

    events (duh).

    http://codebetter.com/blogs/jeremy.miller/archive/2005/09/20/132290.aspxhttp://picocontainer.org/http://picocontainer.org/http://structuremap.sourceforge.net/http://structuremap.sourceforge.net/http://codebetter.com/blogs/jeremy.miller/articles/129546.aspxhttp://codebetter.com/blogs/jeremy.miller/articles/129546.aspxhttp://picocontainer.org/http://structuremap.sourceforge.net/http://codebetter.com/blogs/jeremy.miller/articles/129546.aspxhttp://codebetter.com/blogs/jeremy.miller/archive/2005/09/20/132290.aspx
  • 8/3/2019 The Dependency Injection Pattern

    2/10

    Service A web service proxy class to send requests to the backend

    Presenter The controller class that coordinates all of the above.

    The presenter class without Dependency Injection might look like this.

    public class Presenter

    {

    private View _view;

    private Model _model;

    public Presenter(){}

    public object CreateView(Model model)

    {

    _model = model;

    _view = new View();

    _view.DisplayModel(model);

    return _view;

    }

    public void Close()

    {

    bool canClose = true;

    if (_view.IsDirty())

    {

    canClose = _view.CanCloseDirtyScreen();

    }

    if (canClose)

    {

    _view.Close();

    }

    }

    public void Save()

    {

    Service service = new Service();

    service.Persist(_model);

    }

    }

  • 8/3/2019 The Dependency Injection Pattern

    3/10

    This code cannot be unit tested in isolation because it has a tight coupling to a concrete implementation of

    both the WinForms UserControl (View) and the proxy class to a web service (Service). This code as is

    cannot function without both the User Interface and a web server running the backend. The point of

    using the MVP is to isolate most of the user interface logic away from the WinForms and web service

    mechanics to enable effective unit testing, so were missing something here. To unit test the presenter

    logic wed like to replace the user interface and web service dependencies with aMock object inside our

    test fixture classes. In order to mock the view and service, we first need to use the Dependency Inversion

    Principle to make the Presenter class depend on an abstracted IView and IService interface instead of the

    concrete UserControl and WebProxy classes. The next thing to do is to alter thePresenter class so that we

    can substitute at run time the mock objects instead of the concrete classes within the unit tests. This is

    where Dependency Injection comes into play.

    There are a couple of different flavors of Dependency Injection (viaMartin Fowler + the Pico guys)

    1. Constructor Injection Attach the dependencies through a constructor function at object creation

    2. Setter Injection Attach the dependencies through setter properties

    3. Interface Injection This is an odd duck. Ive never used it or seen this used. I suspect its usage is

    driven by specific DI tools in the Java world.

    4. Service Locator Use a well known class that knows how to retrieve and create dependencies. Not

    technically DI, but this is what most DI/IoC container tools really do.

    Constructor Injection

    My preference is to use the Constructor Injection flavor of DI. The mechanism here is pretty simple;

    just push the dependencies in through the constructor function.

    public class Presenter

    {

    private IView _view;

    private Model _model;

    private IService _service;

    public Presenter(IView view, IService service)

    {

    _view = view;

    http://martinfowler.com/articles/mocksArentStubs.htmlhttp://martinfowler.com/articles/mocksArentStubs.htmlhttp://codebetter.com/blogs/jeremy.miller/articles/129543.aspxhttp://codebetter.com/blogs/jeremy.miller/articles/129543.aspxhttp://martinfowler.com/articles/injection.htmlhttp://martinfowler.com/articles/injection.htmlhttp://martinfowler.com/articles/mocksArentStubs.htmlhttp://codebetter.com/blogs/jeremy.miller/articles/129543.aspxhttp://codebetter.com/blogs/jeremy.miller/articles/129543.aspxhttp://martinfowler.com/articles/injection.html
  • 8/3/2019 The Dependency Injection Pattern

    4/10

    _service = service;

    }

    public object CreateView(Model model){}

    public void Close(){}

    public void Save(){}

    }

    [TestFixture]

    public class PresenterTestFixture

    {

    private IMock _serviceMock;

    private IMock _viewMock;

    private Presenter _presenter;

    [SetUp]

    public void SetUp()

    {

    // Create the dynamic mock classes for IService and IView

    _serviceMock = new DynamicMock(typeof(IService));

    _viewMock = new DynamicMock(typeof(IView));

    // Create an instance of the Presenter class using the mock objects

    _presenter = new Presenter((IView) _viewMock.MockInstance,

    (IService) _serviceMock.MockInstance);

    }

    }

    One of the benefits of using Constructor Injection is that the constructor function now explicitly declares

    the dependencies of a class. I also thing Constructor Injection makes it easier for other developers to use

    your class because it expresses a contract. Give the class what it needs in its constructor function and it

    should be ready to function. Its usually a best practice to create a valid object in as few steps as possible

    for ease of use. Using Constructor Injection also allows you to maintain more encapsulation by

    eliminating the need to expose getter and setter properties for dependencies like the IService interface

    that are immutable.

  • 8/3/2019 The Dependency Injection Pattern

    5/10

    Exposing the dependencies in a constructor function is arguably a violation of encapsulation because now

    a client of Presenter would have to create instances ofIView and IService first before calling the

    constructor function. To get around this issue I usually suggest a compromise. Create a second no

    argument constructor that builds the default instances for clients of Presenter to use. The full

    constructor is usually commented as a testing constructor. Some people think this pattern is evil

    redundancy, but it gets the job done.

    // Testing constructor

    public Presenter(IView view, IService service)

    {

    _view = view;

    _service = service;

    }

    // Default constructor

    public Presenter() : this(new View(), new Service()){}

    Setter Injection

    Setter injection is just creating a setter property to replace a dependency on a previously instantiated

    object. I dont like Setter Injection because it requires extra, hidden steps to prepare an object to

    execute. Ive been burned a couple times in the last year when Ive inherited some code that depended

    on setters to set up dependencies. That being said, Setter Injection does work and is often necessary

    when youre dealing with existing code. Michael Feathers recommends using Setter Injection as a

    dependency breaking technique for legacy code when a dependency is too difficult to expose through a

    constructor.

    Heres the Presenter class using Setter Injection.

    public class Presenter

    {

    private IView _view;

    private Model _model;

    private IService _service;

    public Presenter()

    {

    _view = new View();

    _service = new Service();

  • 8/3/2019 The Dependency Injection Pattern

    6/10

    }

    public IView View

    {

    get { return _view; }

    set { _view = value; }

    }

    public IService Service

    {

    get { return _service; }

    set { _service = value; }

    }

    public object CreateView(Model model){}

    public void Close(){}

    public void Save(){}

    }

    [TestFixture]

    public class PresenterTestFixture

    {

    private IMock _serviceMock;

    private IMock _viewMock;

    private Presenter _presenter;

    [SetUp]

    public void SetUp()

    {

    // Create the dynamic mock classes for IService and IView

    _serviceMock = new DynamicMock(typeof(IService));

    _viewMock = new DynamicMock(typeof(IView));

    // Create an instance of the Presenter class

    _presenter = new Presenter();

    // Attach the Mock objects

  • 8/3/2019 The Dependency Injection Pattern

    7/10

    _presenter.View = (IView) _viewMock.MockInstance;

    _presenter.Service = (IService) _serviceMock.MockInstance;

    }

    }

    Heres a variation on Setter Injection Ive seen other teams use. In the getter of the property, just create

    the default instance of the dependency if it hasnt been created. I dont like this approach because it hides

    the dependencies of a class. I think this is creating Mystery Meat dependencies and brittle code. In

    practice I thought that this made it difficult to retrofit unit tests into existing code. Of course, retrofitting

    unit tests onto existing code is always hard so maybe thats really not a drawback.

    // Always access the _view field through the Property

    public IView View

    {

    get

    {

    if (_view == null)

    {

    _view = new View();

    }

    return _view;

    }

    set { _view = value; }

    }

    Service Locator

    An alternative to using Dependency Injection is to use a Service Locator to fetch the dependency

    objects. Using a Service Locator creates a level of indirection between a class and its

    dependencies. Heres a version of the Presenter class that gets its IViewand IService dependencies by

    asking StructureMaps ObjectFactory class for the default type and configuration

    of IView and IService. While it is generally possible to use the Service Locator to return mock or stub

    objects inside test fixtures, I would still prefer to leave a testing constructor so the unit tests can be

    simpler. I find that using a Service Locator within a unit test can be confusing because its not clear

    where the mock object is used.

    public class Presenter

  • 8/3/2019 The Dependency Injection Pattern

    8/10

    {

    private IView _view;

    private Model _model;

    private IService _service;

    public Presenter()

    {

    // Call to StructureMap to fetch the default configurations of IView and IService

    _view = (IView) StructureMap.ObjectFactory.GetInstance(typeof(IView));

    _service = (IService) StructureMap.ObjectFactory.GetInstance(typeof(IService));

    }

    public object CreateView(Model model){}

    public void Close(){}

    public void Save(){}

    }

    Ive seen several teams go their own way to create custom Service Locators, usually with a Singleton like

    this.

    public class ServiceFactory

    {

    private static IService _instance = new Service();

    private ServiceFactory(){}

    public static IService GetInstance()

    {

    return _instance;

    }

    // Used to register mock or stub instances in place of

    // the concrete Service class

    public static void RegisterInstance(IService instance)

    {

    _instance = instance;

    }

  • 8/3/2019 The Dependency Injection Pattern

    9/10

    }

    I detest this pattern and Ive been eliminating this from my teams primary product. There are just too

    many opportunities to screw up your tests by not having isolated unit tests. Its also unnecessary

    because there are existing tools specifically for this.

    Good for More than Unit Testing

    Ive focused almost entirely on the value of Dependency Injection for unit testing and Ive even bitterly

    referred to using DI as Mock Driven Design. Thats not the whole story though. One of original usages

    for Dependency Injection was to provide smoother migration paths away from legacy code. One

    evolutionary approach to replacing legacy code is the Strangler approach. Making sure that any new

    code that depends on undesirable legacy code uses Dependency Injection leaves an easier migration path

    to eliminate the legacy code later with all new code.

    public interface IDataService{}

    // Now

    public class ProxyToNastyLegacyDataService : IDataService{}

    // Later

    public class CleanNewCodeDataService : IDataService{}

    public class StranglerApplication

    {

    public StranglerApplication(IDataService dataService){}

    }

    Another benefit of Dependency Injection is increasing the potential for reuse later. Going back to the MVP

    architecture, what if you need to replace the heavy WinForms client with an ASP.NET system? The View is

    obviously useless, and I think its probably silly to use a Web Service when a local class will do for

    theIService implementation. We can potentially reuse the Presenter class; just inject a different

    implementation for both IView and IService.

    public class ASPNetView : System.Web.UI.UserControl, IView

    {

    http://www.martinfowler.com/bliki/StranglerApplication.htmlhttp://www.martinfowler.com/bliki/StranglerApplication.html
  • 8/3/2019 The Dependency Injection Pattern

    10/10

    }

    public class LocalService : IService

    {

    }

    public class WebClientMasterController

    {

    public void CreateView(Page page)

    {

    ASPNetView view = (ASPNetView) page.Controls[0];

    Model model = this.GetModel();

    IService service = new LocalService();

    // Presenter can work with a different concrete implementation of

    // IView and IService

    Presenter presenter = new Presenter(view, service);

    presenter.CreateView(model);

    }

    public Model GetModel(){return new Model();}

    }

    Using a Dependency Injection Tool

    Using Dependency Injection potentially adds some overhead to using the classes that dont create their

    own dependencies. This is where one of the Dependency Injection tools can pay large dividends by

    handling this mechanical work and creating some indirection between clients and dependencies. The next

    (and last) post in the whole Inversion of Control chain will look at using a Dependency Injection tool to

    wire up an application.