Difference between dependency injection and dependency inversion

Istiaque Ahmed picture Istiaque Ahmed · Oct 12, 2017 · Viewed 36.7k times · Source

Two design patterns namely Dependency Injection and Dependency Inversion exist out there, Articles are there in the net trying to explain the difference. But the need to explain it in easier words is still there. Any one out there to come up to ?

I need to understand it in PHP.

Answer

Ben Cottrell picture Ben Cottrell · Oct 14, 2017

(Note: This answer is language-agnostic, although the question specifically mentions PHP, but being unfamiliar with PHP, I have not provided any PHP examples).

Terminology - Dependencies and Coupling

In the context of Object-Oriented Programming, a dependency is any other object type which a class has a direct relationship with. When a class depends directly upon another object type, it can be described as being coupled to that type.

In general, any type used by a class is a dependency to some extent. There are many different ways for a class to depend upon another type, including:

  • Object types used by Instance variables
  • Object types used by Constructor parameters
  • Object types used by Accessor/Mutator methods
  • Constructors (And sometimes methods) which create new objects directly
  • Inheritance

The stronger the relationship between a class and its dependency, the tighter the coupling; therefore when a class depends directly upon another concrete class (such as the case with inheritance creating a direct dependency on a base class, or the case where a constructor creates new objects for its instance variables), any future changes to that direct dependency are more likely to "ripple" across in a Butterfly-effect style.


Difference Between Injection vs Inversion

  • Dependency Injection is an Inversion of Control technique for supplying objects ('dependencies') to a class by way of the Dependency Injection Design Pattern. Typically passing dependencies via one of the following:

    • A constructor
    • A public property or field
    • A public setter
  • The Dependency Inversion Principle (DIP) is a software design guideline which boils down to two recommendations about de-coupling a class from its concrete dependencies:

    1. 'High-level modules should not depend on low-level modules. Both should depend on abstractions.'
    2. 'Abstractions should not depend upon details. Details should depend upon abstractions.'

Or, to put it even more succinctly:

  • Dependency Injection is an implementation technique for populating instance variables of a class.
  • Dependency Inversion is a general design guideline which recommends that classes should only have direct relationships with high-level abstractions.

Dependency Injection and Inversion of Control (IoC)

Dependency Injection applies the the IoC principle by ensuring classes are never responsible for creating or supplying their own dependencies (and therefore aren't responsible for the lifetime of those dependencies either).

However, IoC is not Dependency Injection - indeed, IoC as a principle has nothing particularly to do with dependencies or dependency injection per-se; Dependency Injection is a design pattern based around the principle of IoC.

IoC is seen in many other contexts, including those totally unrelated to object creation or dependencies, such as message passing via a Mediator or Message pump to trigger event handlers. Other (unrelated) examples of IoC include:

  • A windowed application using event handler functions/methods to handle mouse/keyboard input events.
  • An MVC web application using Controller Actions to handle HTTP requests.

(Updated from the original answer as a separate explanation about IoC)


Dependency Injection Pattern

Dependency Injection is a design pattern which applies the IoC principle to ensure that a class has absolutely no involvement or awareness in the creation or lifetime of objects used by its constructor or instance variables -- the "common" concern about object creation and populating instance variables is deferred to a framework instead.

That is to say, a class may specify its instance variables, but does not do any work to populate those instance variables (with the exception of using constructor parameters as a "pass-through")

A class which is designed with Dependency Injection in mind may look like this:

// Dependency Injection Example...

class Foo {
    // Constructor uses DI to obtain the Meow and Woof dependencies
    constructor(fred: Meow, barney: Woof) {
        this.fred = fred;
        this.barney = barney;
    }
}

In this example, Meow and Woof are both dependencies injected via the Foo constructor.

On the other hand, a Foo class which is designed without Dependency Injection might simply create the Meow and Woof instances itself, or perhaps use some kind of service locator/factory:

// Example without Dependency Injection...

class Foo {
    constructor() {
        // a 'Meow' instance is created within the Foo constructor
        this.fred = new Meow();

        // a service locator gets a 'WoofFactory' which in-turn
        // is responsible for creating a 'Woof' instance.
        // This demonstrates IoC but not Dependency Injection.
        var factory = TheServiceLocator.GetWoofFactory();
        this.barney = factory.CreateWoof();
    }
}

So dependency injection simply means that a class has deferred responsibility of obtaining or providing its own dependencies; instead that responsibility resides with whatever wants to create an instance. (Which is usually an IoC Container)


Dependency Inversion Principle (DIP)

Dependency Inversion is broadly about de-coupling concrete classes by preventing those classes having any direct reference to each other.

The DIP is primarily concerned with ensuring that a class only depends upon higher-level abstractions. For example, interfaces exist at a higher level of abstraction than a concrete class.

The DIP is not about injecting dependencies, although the dependency injection pattern is one of many techniques which can help provide the level of indirection needed to avoid depending on low-level details and coupling with other concrete classes.

Note: Dependency Inversion is often more explicit in statically typed programming languages such as C# or Java, because those languages enforce strict type-checking on variable names. On the other hand, Dependency Inversion is already passively available in dynamic languages such as Python or JavaScript because variables in those languages do not have any particular type restrictions.

Consider a scenario in a statically typed language where a class requires the ability to read a record from the application's database:

// class Foo depends upon a concrete class called SqlRecordReader.

class Foo {
    reader: SqlRecordReader;

    constructor(sqlReader: SqlRecordReader) {
        this.reader = sqlReader;
    }

    doSomething() {
        var records = this.reader.readAll();
        // etc.
    }
}

In the above example, and despite the use of Dependency Injection, class Foo still has a hard dependency on SqlRecordReader, yet the only thing it really cares about is that there exists a method called readAll() which returns some records.

Consider the situation where SQL database queries are later refactored out into separate micro-services requiring a change to the codebase; the Foo class would need to read records from a remote service instead. Or alternatively, a situation where Foo unit tests need to read data from an in-memory store or a flat file.

If, as its name suggests, the SqlRecordReader contains database and SQL logic, any move to microservices would need the Foo class to change.

Dependency Inversion guidelines suggest that SqlRecordReader should be replaced with a higher-level abstraction which only provides the readAll() method. i.e:

interface IRecordReader {
    Records[] getAll();
}

class Foo {
    reader: IRecordReader;

    constructor(reader: IRecordReader) {
        this.reader = reader;
    }
}

As per DIP, the IRecordReader is a higher-level abstraction than SqlRecordReader, and forcing Footo depend onIRecordReaderinstead ofSqlRecordReader` satisfies DIP guidelines.


Why DIP Guidelines are Useful

The keyword is guideline - dependency inversion adds indirection to the design of your program. The obvious disadvantage of adding any kind of indirection is that the complexity (i.e. the cognitive "load" required for a human to understand what's going on) increases.

In many cases, indirection can make code easier to maintain (fix bugs, add enhancements) however:

In this last example, Foo might receive a SqlRecordReader, or maybe a SoapRecordReader, or perhaps a FileRecordReader, or maybe even for unit testing a MockRecordReader - the point is that it doesn't know or care anything about different possible implementations of IRecordReader - provided of course those implementations live up to the Liskov Substitution Principle.

Furthermore, it avoids the potentially dirty scenario where a developer who is in a rush to get something working might consider trying to "fudge" the Liskov principle by inheriting the SoapRecordReader or FileRecordReader from a base class SqlRecordReader.

Worse still, an inexperienced developer might even change the SqlRecordReader itself so that class has logic not only for SQL but also for SOAP endpoints, The Filesystem, and anything else which might be needed. (This kind of thing happens too often in the real world - especially in poorly maintained code, and is nearly always a Code Smell.)