Dependency Injection pattern

Forms of Dependency Injection

Forms of Dependency Injection

Dependency Injection pattern

DI (Dependency Injection) :- DI is a sub-type of IoC and is implemented by constructor injection, setter injection or Interface injection.

The Dependency-Injection (DI) pattern is a more specific version of IoC pattern. But DI is only one form of IoC. Inversion of Control as a concept can be applied more broadly than just injecting dependencies in a constructor method.

“Dependency Injection” is a 25-dollar term for a 5-cent concept.

Dependency injection means giving an object its instance variables.

With Dependency Injection, the caller will pass in the dependency. Hence the caller (or a higher up caller) instantiates the dependency. The caller controls the dependencies.

The control of where a dependency is instantiated has been inverted - instead of being at the “bottom”, where the code that needs it exists, it is instantiated at the “top”, where the code that needs it is being called.

Dependency injection is all about removing dependencies from your code.

Every DI implementation is an IoC. But vice-versa is not true.

The control of the dependencies is inverted from one being called to the one calling.

In Dependency Injection pattern, connecting objects with other objects, or “injecting” objects into other objects, is done by an assembler rather than by the objects themselves.

Example 1 for dependency injection

Lets say that your application has a text-editor component, and you want to provide spell checking. The traditional procedural code would look something like this. What it is doing here is, it creates a dependency between the TextEditor and the SpellChecker.

Traditional programming: In this example, we are instantiating SpellChecker (this.checker = new SpellChecker();), which means the TextEditor class directly instantiates (depends on) the SpellChecker class.

public class TextEditor {
    private SpellChecker checker;
    public TextEditor() {
        this.checker = new SpellChecker();
    }
}

In an IoC scenario, we would instead do something like this.

public class TextEditor {
    private IocSpellChecker checker;
    public TextEditor(IocSpellChecker checker) {
        this.checker = checker;
    }
}

In the second code example we are creating an abstraction by having the SpellChecker dependency class in TextEditor’s constructor signature (not initializing dependency in the class directly). This allows us to call the dependency, and then pass it to the TextEditor class like so:

SpellChecker sc = new SpellChecker(); // dependency
TextEditor textEditor = new TextEditor(sc);

Now the client creating the TextEditor class has control over which SpellChecker implementation to use because we’re injecting the dependency into the TextEditor signature.

Example 2 for dependency injection

Classic code (without Dependency injection)

Here is how a code not using DI will roughly work:

  1. Application needs Foo (e.g. a controller), so:
  2. Application creates Foo
  3. Application calls Foo
    1. Foo needs Bar (e.g. a service), so:
    2. Foo creates Bar
    3. Foo calls Bar
      1. Bar needs Bim (a service, a repository, …), so:
      2. Bar creates Bim
      3. Bar does something

Using dependency injection

Here is how a code using DI will roughly work:

  1. Application needs Foo, which needs Bar, which needs Bim, so:
  2. Application creates Bim
  3. Application creates Bar and gives it Bim
  4. Application creates Foo and gives it Bar
  5. Application calls Foo
    1. Foo calls Bar
      1. Bar does something

What problems does Dependency Injection solve?

Dependency injection makes it easy to swap with the different implementation of the injected classes. While unit testing you can inject a dummy implementation, which makes the testing a lot easier.

Ex: Suppose your application stores the user uploaded file in the Google Drive, with DI your controller code may look like this:

class SomeController
{
    private $storage;
    function __construct(StorageServiceInterface $storage)
    {
        $this->storage = $storage;
    }
    public function myFunction ()
    {
        return $this->storage->getFile($fileName);
    }
}
class GoogleDriveService implements StorageServiceInterface
{
    public function authenticate($user) {}
    public function putFile($file) {}
    public function getFile($file) {}
}

When your requirements change say, instead of GoogleDrive you are asked to use the Dropbox. You only need to write a dropbox implementation for the StorageServiceInterface. You don’t have make any changes in the controller as long as Dropbox implementation adheres to the StorageServiceInterface.

While testing you can create the mock for the StorageServiceInterface with the dummy implementation where all the methods return null(or any predefined value as per your testing requirement).

Instead if you had the controller class to construct the storage object with the new keyword like this:

class SomeController
{
    private $storage;
    function __construct()
    {
        $this->storage = new GoogleDriveService();
    }
    public function myFunction ()
    {
        return $this->storage->getFile($fileName);
    }
}

When you want to change with the Dropbox implementation you have to replace all the lines where new GoogleDriveService object is constructed and use the DropboxService. Besides when testing the SomeController class the constructor always expects the GoogleDriveService class and the actual methods of this class are triggered.

When is it appropriate and when not? We use DI when we think there are (or there can be) alternative implementations of a class.