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.
(Note: This answer is language-agnostic, although the question specifically mentions PHP, but being unfamiliar with PHP, I have not provided any PHP examples).
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:
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.
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:
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:
Or, to put it even more succinctly:
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:
(Updated from the original answer as a separate explanation about IoC)
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 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 on
IRecordReaderinstead of
SqlRecordReader` satisfies DIP guidelines.
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.)