Skip to main content

Dependency Injection

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that is be used in a class as service. An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
Dependency injection allows a program design to follow the dependency inversion principle. The client delegates to external code (the injector) the responsibility of providing its dependencies. The client is not allowed to call the injector code. It is the injecting code that constructs the services and calls the client to inject them. This means the client code does not need to know about the injecting code. The client does not need to know how to construct the services. The client does not need to know which actual services it is using. The client only needs to know about the intrinsic interfaces of the services because these define how the client may use the services. This separates the responsibilities of use and construction.
Dependency injection separates the creation of a client’s dependencies from the client’s behavior, which allows program designs to be loosely coupled and to follow the dependency inversion and single responsibility principles. It directly contrasts with the service locator pattern, which allows clients to know about the system they use to find dependencies.
An injection, the basic unit of dependency injection, is not a new or a custom mechanism. It works in the same way that “parameter passing” works. Referring to “parameter passing” as an injection carries the added implication that it’s being done to isolate the client from details.
Dependency injection involves four roles:
  • the service object(s) to be used
  • the client object that is depending on the services it uses
  • the interface that define how the client may use the services
  • the injector, which is responsible for constructing the services and injecting them into the client
Any object that may be used can be considered a service. Any object that uses other objects can be considered a client. The names have nothing to do with what the objects are for and everything to do with the role the objects play in any one injection.
The interfaces are the types the client expects its dependencies to be. hey may truly be interface types implemented by the services but also may be abstract classes.
Given that the client has no concrete knowledge, so long as the what the client uses, the interfaces name and methods, then the client won’t need to change even if what is behind the interface changes. However, if the interface is refactored from being a class to an interface type (or vice versa) the client will need to be recompiled.This is significant if the client and services are published separately. This unfortunate coupling is one that dependency injection cannot resolve.
The injector introduces the services into the client. Often, it also constructs the client. An injector may connect together a very complex object graph by treating an object like a client and later as a service for another client. The injector may be referred to by other names such as: assembler, provider, container, factory, builder, spring, construction code, or main.

Advantages

  • Dependency injection allows a client to remove all knowledge of a concrete implementation that it needs to use. This helps isolate the client from the impact of design changes and defects. It promotes reusability, testability and maintainability.
  • Reduction of boilerplate code in the application objects since all work to initialize or set up dependencies is handled by a provider component.
  • Dependency injection allows concurrent or independent development. Two developers can independently develop classes that use each other, while only needing to know the interface the classes will communicate through. Plugins are often developed by third party shops that never even talk to the developers who created the product that uses the plugins.
  • Dependency Injection decreases coupling between a class and its dependency.

Disadvantages

  • Dependency injection creates clients that demand configuration details be supplied by construction code. This can be onerous when obvious defaults are available.
  • Dependency injection can make code difficult to trace (read) because it separates behavior from construction. This means developers must refer to more files to follow how a system performs.
  • Dependency injection forces complexity to move out of classes and into the linkages between classes which might not always be desirable or easily managed.
  • Ironically, dependency injection can encourage dependence on a dependency injection framework.
In the following C# example, the Client class contains a Service member variable that is initialized by the Client constructor. The client controls which implementation of service is used and controls its construction. In this situation, the client is said to have a hard-coded dependency on ServiceExample.
// An example without dependency injection
public class Client {
    // Internal reference to the service used by this client
    private Service service;

    // Constructor
    public Client() {
        // Specify a specific implementation in the constructor 
        //instead of using dependency injection
        service = new ServiceExample();
    }

    // Method within this client that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}
Dependency injection is an alternative technique to initialize the member variable rather than explicitly creating a service object as shown above.

Three types of dependency injection

There are at least three ways an object can receive a reference to an external module:
  • constructor injection: the dependencies are provided through a class constructor.
  • property injection: the client exposes a property that the injector uses to inject the dependency.
  • interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a property that accepts the dependency.

Constructor injection

This method requires the client to provide a parameter in a constructor for the dependency.
// Constructor
Client(Service service) {
    // Save the reference to the passed-in service inside this client
    this.service = service;
}

Setter injection

This method requires the client to provide a property for each dependency.
// Property
public void setService(Service service) {
    // Save the reference to the passed-in service inside this client
    this.service = service;
}

Interface injection

This is simply the client publishing a role interface to the property of the client’s dependencies. It can be used to establish how the injector should talk to the client when injecting dependencies.
// Service interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client : ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

Comments

Popular posts from this blog

gcAllowVeryLargeObjects Element

There are numerous new features coming with .NET 4.5 and here, on this blog, you can find several posts about it. But the feature we are goint to talk about today is very exciting, because we were waiting for it more than 10 years. Since .NET 1.0 the memory limit of .NET object is 2GB. This means you cannot for example create array which contains elements with more than 2GB in total. If try to create such array, you will get the OutOfMemoryException. Let’s see an example how to produce OutOfMemoryException. Before that Open Visual Studio 2012, and create C# Console Application, like picture below. First lets create simple struct with two double members like example below: 1 2 3 4 5 6 7 8 9 10 11 12 public struct ComplexNumber {      public double Re;      public double Im;      public ComplexNumber( double re, double im)      {    ...

Support for debugging lambda expressions with Visual Studio 2015

Anyone who uses LINQ (or lambdas in general) and the debugger will quickly discover the dreaded message “Expression cannot contain lambda expressions”. Lack of lambda support has been a limitation of the Visual Studio Debugger ever since Lambdas were added to C# and Visual Basic.  With visual studio 2015 Microsoft has added support for debugging lambda expressions. Let’s first look at an example, and then I’ll walk you through current limitations. Example To try this yourself, create a new C# Console app with this code: using System.Diagnostics; using System.Linq; class Program { static void Main() { float[] values = Enumerable.Range(0, 100).Select(i => (float)i / 10).ToArray(); Debugger.Break(); } } Then compile, start debugging, and add “values.Where(v => (int)v == 3).ToArray()” in the Watch window. You’ll be happy to see the same as what the screenshot above shows you. I am using Visual Studio 2015 Preview and it has some limitati...

An Introduction to Windows Azure Table Storage

Windows Azure Tables are a non-relational, key-value-pair, storage system suitable for storing massive amounts of unstructured data.  Whereas relational stores such as SQL Server, with highly normalized designs, are optimized for storing data so that queries are easy to produce, the non-relational stores like Table Storage are optimized for simple retrieval and fast inserts.  This article will cover the very basics of Windows Azure Table storage and provide you with resources and suggested topics to continue your learning. Some people, when first learning about the Windows Azure platform, find it hard to understand the purpose of the Table Storage feature.  This is especially true of those who are familiar with developing applications using highly relational data.  To get a good understanding of how a Key-Value Pair system differs from a traditional relational database you can read Buck Woody’s article on the topic in his continuing series:...