PHP Dependency Injection:
Providing dependencies of one object to another object called Dependency Injection.
Dependency Injection involves four roles:
- A service object(s) to be used.
- A client object(s) that is depending upon service.
- An interface that define how client can use service
- An injector, which is responsible for constructing services and injecting them into clients
Advantages:
- Allows independent development through which developer can implement classes that use each other with an abstraction layer called interface.
- Decreases coupling between classes and its dependency.
- Promotes re-usability, testability, maintainability.
Disadvantages:
- Forces complexity to move out of classes and into the linkage between the classes which is not desierable or not easily understandable
- Encourage to depend on Dependency Injection Framework.
- It is difficult to read or trace how flow works.
PHP Dependency Injection and Dependency Injection Content are two different things. So in this article I will only explain Dependency Injection with some example. So if you want to know PHP Dependency Injection then needs to know Dependency Inversion Principle.
Dependency Inversion Principle:
The control of dependencies reverted from one being called to the one calling.
HighLeven Class => Abstraction Layer => LowLevel Class.
Lets talk about this calling principle with class levels. Generally in software application, we see different level of classes:
- Low level class (Implements basics and primary operations).
- High level class (encapsulate all complex logic)
For an example one module called Pagecreation which will take character from keyboard and writes them into printer device.
- High level class: createPage class contains logic of creating page
- Low level class are readFromKeyBoard, writeToPrinter
In bad design , the high level classes heavily depends on low level classes. In such cases if we want to change or add some extra feature in writeToPrinter class after implementation then we have to change createPage class also. We can change if not complex one, lets think if it is a complex one then it will be really hard to change and all time we will rub our head.
For avoiding such problem we can implements an abstraction layer like interface in between high level and low level.
- Highlevel class should not depends upon low level class, bot the should depends upon abstraction
- Abstraction should not depends upon details . Details should depends upon abstraction.
HighLeven Class => Abstraction Layer => LowLevel Class.HighLeven Class => Abstraction Layer => LowLevel Class.
We can also differentiate classic php codes with dependency injection code.
Classic PHP code:
This is how code is not using DI
DI PHP code:
This is how code is using DI
Understanding with example:
Example of comparing a classic implementation(using new or singletons) Vs using DI
classic implementation:
Lets say we have:
class GoogleMaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } } class OpenStreetMaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } }
and
class StoreService { public function getStoreCoorinate($store){ $gMaps = new GoogleMaps(); or //$gMaps = GoogleMaps::getInstance(); return $gMaps->getCoordinateInfo($store->getAddress); } }
Now we want use OpenStreetMaps instead of GoogleMaps. I can simply change but what issue is , we have to change the code in StoreService and allother class which uses GoogleMaps. What simplifies here is that , without DI all codes are tightly coupled each other.
Using DI:
class StoreService { private $gMaps; public function __construct(Gmaps $gMaps){ $this->gMaps = $gMaps; } public function getStoreCoorinate($store){ return $this->gMaps->getCoordinateInfo($store->getAddress); } }
and the services that are defined using Interface.
interface Gmaps { public function getCoordinateInfo ($address); } class GoogleMaps implements Gmaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } } class OpenStreetMaps implements Gmaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } }
Now it is for the user of StoreService which implementation need to use . It can be changed anytime without having rewrite to StoreService. Here classes are no longer tightly coupled each other.
So we can say there are three types of Injection
- Constructor Injection
- Setter(Method) Injection:
- Interface Injection:
Constructor Injection:
This methods requires the client to provide a parameter inside Constructor for dependency
public function __construct(Gmaps $gMaps){ $this->gMaps = $gMaps; }
Setter(Method) Injection:
This methods requires the client to provide setter method for dependency
public function setGmaps(Gmaps $gMaps){ $this->gMaps = $gMaps; }
Interface Injection:
It is used as abstraction layer , how the injector should talk to client while injecting dependencies.
class StoreService { private $gMaps; public function __construct(Gmaps $gMaps){ $this->gMaps = $gMaps; } public function getStoreCoorinate($store){ return $this->gMaps->getCoordinateInfo($store->getAddress); } }
and the services that are define using Interface.
interface Gmaps { public function getCoordinateInfo ($address); } class GoogleMaps implements Gmaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } } class OpenStreetMaps implements Gmaps { public function getCoordinateInfo ($address) { //return co-ordinate information by address } }