Dependency injection is a set of software design principles and patterns that enables us to develop loosely coupled code, it is also one of the most misunderstood concepts in the object oriented programming.
When class A uses some functionality of class B, then its said that class A has a dependency of class B.
The main purpose of the DI is the maintainability through the loose coupling technique, here are some benefits gained form the loose coupling:
Late binding
Services can be swapped with other services, late binding refers to the ability to replace parts of an application without recompiling code
Extensibility and Maintainability
Code can be extended and reused, and the responsibility of each class becomes clearly defined. Finally, adding a new feature is more easy because the structure of code allows developers to understand where apply changes.
Testing
DI is an important part of supporting unit testing. Unit tests provide rapid feedback on the state of an application, but it is only possible to write unit test when the unit can be properly isolated from it’s dependencies.
By using DI, developers have the capability to inject dependencies and mocks inside the application code to test a specific part of the application without affecting others.
Dependency injection patterns
The Dependency injection patterns describe different ways to implement the Dependency injection inside our application. The most important is the Constructor injection pattern.
There are basically three types of dependency injection:
- constructor injection: the dependencies are provided through a class constructor.The class that uses the dependency exposes a public constructor that takes an instance of the required dependency as a constructor argument. It is good practice to mark the field holding the dependency as readonly, this guarantees that once the initialization logic of the constructor has executed: the field can’t be modified.
- setter injection: the client exposes a setter method 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 setter method that accepts the dependency.
Disadvantages of DI
- It’s a bit complex to learn, and if overused can lead to management issues and other problems.
- Many compile time errors are pushed to run-time.
- Dependency injection frameworks are implemented with reflection or dynamic programming. This can hinder use of IDE automation, such as “find references”, “show call hierarchy” and safe refactoring.
You can implement dependency injection on your own (Pure Vanilla) or use third-party libraries or frameworks.
Dependency Injection in Magento 2
Magento 2 uses Dependency Injection to replace functionality provided by the Mage class in Magento 1.x.
the dependency inversion principle and use abstractions in your code to reduce code dependencies. This means that your high level classes should use the interfaces of low level classes instead of working with them directly.
Using interfaces in your code reduces the risk of incompatibility bugs when Magento changes the underlying implementation of those interfaces. It also lets you focus on what a class does instead of how its implemented.
Since the Magento codebase follows this principle, you can map your own implementation of a Magento interface to a dependent class or service using the di.xml file.
Magento uses class constructor signatures to retrieve information about an object’s constructor dependencies. When a class is constructed, the object manager injects the class’s dependencies, defined in the di.xml file, into the class constructor.
Injection types used in Magento
The following code sample highlights the two types of dependency injections used in Magento:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="moduleConfig" type="Magento\Core\Model\Config">
<arguments>
<argument name="type" xsi:type="string">system</argument>
</arguments>
</virtualType>
<type name="Magento\Core\Model\App">
<arguments>
<argument name="config" xsi:type="object">moduleConfig</argument>
</arguments>
</type>
</config>
Constructor injection
In the code sample, the Builder class declares its dependency on the Factory and Menu classes in its constructor. Magento uses the di.xml file to determine which implementations to inject into the Builder class.
Method injection
In the code sample, the Builder class is also dependent on the CommandAbstract class for its processCommand() method.