Mehedi Hasan's BLog

Tuesday, October 1, 2019

OOP : Dependency Injection in PHP

This is what Wikipedia has to say about Dependency Injection:
In software engineering, dependency injection is a software design pattern that implements inversion of control for software libraries. Caller delegates to an external framework the control flow of discovering and importing a service or software module specified or "injected" by the caller.
In simple terms, Dependency Injection is a design pattern that helps avoid hard-coded dependencies for some piece of code or software.
The dependencies can be changed at run time as well as compile time. We can use Dependency Injection to write modular, testable and maintainable code:
  • Modular: The Dependency Injection helps create completely self-sufficient classes or modules
  • Testable: It helps write testable code easily eg unit tests for example
  • Maintainable: Since each class becomes modular, it becomes easier to manage it

The Problem

We have dependencies almost always in our code. Consider the following procedural example which is pretty common:
function getUsers() {
     global $database;
     return $database->getAll('users');
}
Here the function getUsers has dependency on the $database variable (tight coupling). It has some of these problems:
  • The function getUsers needs a working connection to some database . Whether there is successful connection to database or not is the fate of getUsers function
  • The $database comes from outer scope so chances are it might be overwritten by some other library or code in the same scope in which case function may fail
Of course you could have used the try-catch constructs but it still doesn't solve the second problem.
Let's consider another example for a class:
class User 
{
    private $database = null;

    public function __construct() {
        $this->database = new database('host', 'user', 'pass', 'dbname');
    }

    public function getUsers() {
        return $this->database->getAll('users');
    }
}

$user = new User();
$user->getUsers();
This code again has these problems:
  • The class User has implicit dependency on the specific database. All dependencies should always be explicit not implicit. This defeats Dependency inversion principle
  • If we wanted to change database credentials, we need to edit the User class which is not good; every class should be completely modular or black box. If we need to operate further on it, we should actually use its public properties and methods instead of editing it again and again. This defeats Open/closed principle
  • Let's assume right now class is using MySQL as database. What if we wanted to use some other type of database ? You will have to modify it.
  • The User class does not necessarily need to know about database connection, it should be confined to its own functionality only. So writing database connection code in User class doesn't make it modular. This defeats the Single responsibility principle. Think of this analogy: A cat knows how to meow and a dog knows how to woof; you cannot mix them or expect dog to say meow. Just like real world, each object of a class should be responsible for its own specific task.
  • It would become harder to write unit tests for the User class because we are instantiating the database class inside its constructor so it would be impossible to write unit tests for the User class without also testing the database class.

Enter Dependency Injection!

Let's see how we can easily take care of above issues by using Dependency Injection. The Dependency Injection is nothing but injecting a dependency explicitly. Let's re-write above class:
class User 
{
    private $database = null;

    public function __construct(Database $database) {
        $this->database = $database;
    }

    public function getUsers() {
        return $this->database->getAll('users');
    }
}

$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
$user->getUsers();
And there you have much better code, thanks to Dependency Injection principle. Notice that instead of hard-coding database dependency:
$this->database = new database('host', 'user', 'pass', 'dbname');
We are now injecting it into the constructor, that's it:
public function __construct(Database $database)
Notice also how we are passing database instance now:
$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User($database);
$user->getUsers();
It follows Hollywood Principle, which states: "Don’t call us, we’ll call you."
Let's see if this explicit dependency injection now solves problems we mentioned above.

The class User has implicit dependency on the specific database . All dependencies should always be explicit not implicit. This defeats Dependency inversion principle
We have already made database dependency explicit by requiring it into the constructor of the User class:
public function __construct(Database $database)
Here we are taking advantage of type hinting by specifying type of object we are expecting which is Database although it wasn't necessary but it is always a good idea to type hint when you can.
If we wanted to change database credentials, we need to edit the User class which is not good; every class should be completely modular or black box. If we need to operate further on it, we should actually use its public properties and methods instead of editing it again and again. This defeats Open/closed principle
The User class now does not need to worry about how database is connected. All it expects is Database instance. We no more need to edit User class for it's dependency, we have just provided it with what it needed.
Let's assume right now class is using MySQL as database. What if we wanted to use some other type of database ? You will have to modify it.
Again, the User class doesn't need to know which type of database is used. For the Database, we could now create different adapters for different types of database and pass to User class. For example, we could create an interface that would enforce common methods for all different types of database classes that must be implement by them. For our example, we pretend that interface would enforce to have a getUser() method requirement in different types of database classes.
The User class does not necessarily need to know about database connection, it should be confined to its own functionality only. So writing database connection code in User class doesn't make it modular. This defeats the Single responsibility principle.
Of course User class now doesn't know how database was connected. It just needs a valid connected Database instance.
It would become harder to write unit tests for the User class because we are instantiating the database class inside its constructor so it would be impossible to write unit tests for the User class without also testing the database class.
If you have wrote unit tests, you know now it will be a breeze to write tests for the User class using something like Mockery or similar to create mock object for the Database.

Different Ways of Injecting Dependencies

Now that we have seen how useful Dependency Injection is, let's see different ways of injecting dependencies. There are three ways you can inject dependencies:
  • Constructor Injection
  • Setter Injection
  • Interface Injection
Constructor Injection
We have already seen example of Constructor Injection in above example. Constructor injection is useful when:
  • A dependency is required and class can't work without it. By using constructor injection. we make sure all its required dependencies are passed.
  • Since constructor is called only at the time of instantiating a class, we can make sure that its dependencies cant be changed during the life time of the object.
Constructor injection suffer from one problem though:
  • Since constructor has dependencies, it becomes rather difficult to extend/override it in child classes.
Setter Injection
Unlike Constructor injection which makes it required to have its dependencies passed, setter injection can be used to have optional dependencies. Let's pretend that our User class doesn't require Database instance but uses optionally for certain tasks. In this case, you would use a setter method to inject the Database into the User class something like:
class User 
{
    private $database = null;

    public function setDatabase(Database $database) {
        $this->database = $database;
    }

    public function getUsers() {
        return $this->database->getAll('users');
    }
}

$database = new Database('host', 'user', 'pass', 'dbname');
$user = new User();
$user->setDatabase($database);
$user->getUsers();
As you can see, here we have used setDatabase() setter function to inject Database dependency into the User class. If we needed some other dependency, we could have created one more setter method and injected in the similar fashion.
So Setter Injection is useful when:
  • A class needs optional dependencies so it can set itself up with default values or add additional functionality it needs.
Notice that you could also inject dependency via public property for a class. So instead of using setter function $user->setDatabase($database);, you could also do $user->database = new Database(...);
Interface Injection
In this type of injection, an interface enforces the dependencies for any classes that implement it, for example:
interface someInterface {
    function getUsers(Database $database);
}
Now any class that needs to implement someInterface must provide Database dependency in their getUsers() methods.

The Problem Again

So for we have seen very contrived example of injecting dependency into a simple class but in real world applications, a class might have many dependencies. It isn't all that easy to manage all those dependencies because you need to KNOW which dependencies are required by a certain class and HOW they need to be instantiated. Let's take example of setter injection:
class User 
{
    private $database = null;

    public function setDatabase(Database $database) {
        $this->database = $database;
    }

    public function getUsers() {
        return $this->database->getAll('users');
    }
}
Since dependencies in this case are optional, we could have mistakenly written this code to get users:
$user = new User();
$user->getUsers();
Since we didn't know getUsers() method is actually dependent on Database class, this would have given error. You could have found that out only by going to code of User class and then realizing there is setDatabase() method that must be called before using the getUsers() method. Or let's assume further that before using database, we needed to set some type of configuration for the User class like:
$user = new User();
$user->setConfig($configArray);
Then again we needed to remember specific order of method calls:
$user = new User();
$user->setConfig($configArray);
$user->setDatabase($database);
So you must remember order of method calls, you can't use database if you don't setup configuration first, so you can't do:
$user = new User();
$user->setDatabase($database);
$user->setConfig($configArray);
This is example for setter injection but even with constructor injection if there are many dependencies, it becomes harder to manage all of those manually and you could easily and mistakenly create more than one instances of dependencies throughout your code which would result in high memory usage.
You might wonder dependency injection sounded like good thing to have but these problems are not worth it. Well that's not true because there is solution to all of these problems discussed next :)

Solution - Dependency Injection Container

Of course it would be difficult to manage dependencies manually; this is why you need a Dependency Injection Container. A Dependency Injection Container is something that handles dependencies for your class(es) automatically. If you have worked with Laravel or Symfony, you know that their components have dependencies on on other classes. How do they manage all of those dependencies ? Yes they use some sort of Dependency Injection Container.
There are quite some dependency injection containers out there for PHP that can be used for this purpose or you can also write your own. Each container might have bit of different syntax but they perform the same thing under the hood.

So in conclusion, you must always remove hard-coded dependencies from your code and inject them using Dependency Injection instead for its benefits and then have all the injected dependencies managed automatically for you by using some dependency injection container
http://codeinphp.github.io/post/dependency-injection-in-php/
at October 01, 2019
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest

No comments:

Post a Comment

Newer Post Older Post Home
Subscribe to: Post Comments (Atom)

PostgreSQL (postgre) Setup on macOS 12 (MacBook Pro 2016) Without Homebrew setup process

  • Vuetify — Color and Date Pickers
    Vuetify is a popular UI framework for Vue apps. In this article, we’ll look at how to work with the Vuetify framework. Color Picker Inputs W...
  • Laravel API Integration Best Practices for Clean and Maintainable Code
    In today’s software development landscape, APIs (Application Programming Interfaces) are essential for enabling communication between differ...
  • Algorithms Behind JavaScript Array Methods
    concat() join() fill() includes() indexOf() reverse() sort() splice() at() copyWithin() flat() Array.from() findLastIndex() forEach() every(...

About Me

My photo
Shariful Islam (Mehedi)
Bangladesh
Hi I am shariful Islam, A Full Stack Laravel Vue js and React JS developer. I am a software engineer having 7+ years of experience. You cantact me at: +8801679174124 Email: sharifcse57@gmail.com
View my complete profile

Post Updates

  • ►  2025 (6)
    • ►  June 2025 (1)
      • ►  Jun 24 (1)
    • ►  May 2025 (1)
      • ►  May 06 (1)
    • ►  March 2025 (1)
      • ►  Mar 15 (1)
    • ►  January 2025 (3)
      • ►  Jan 21 (1)
      • ►  Jan 17 (1)
      • ►  Jan 01 (1)
  • ►  2024 (6)
    • ►  November 2024 (1)
      • ►  Nov 04 (1)
    • ►  October 2024 (1)
      • ►  Oct 05 (1)
    • ►  September 2024 (1)
      • ►  Sep 25 (1)
    • ►  June 2024 (1)
      • ►  Jun 05 (1)
    • ►  March 2024 (1)
      • ►  Mar 01 (1)
    • ►  February 2024 (1)
      • ►  Feb 12 (1)
  • ►  2023 (16)
    • ►  November 2023 (1)
      • ►  Nov 30 (1)
    • ►  October 2023 (3)
      • ►  Oct 31 (2)
      • ►  Oct 09 (1)
    • ►  August 2023 (1)
      • ►  Aug 02 (1)
    • ►  July 2023 (1)
      • ►  Jul 02 (1)
    • ►  June 2023 (1)
      • ►  Jun 03 (1)
    • ►  March 2023 (3)
      • ►  Mar 04 (3)
    • ►  February 2023 (1)
      • ►  Feb 11 (1)
    • ►  January 2023 (5)
      • ►  Jan 22 (3)
      • ►  Jan 17 (1)
      • ►  Jan 16 (1)
  • ►  2022 (67)
    • ►  December 2022 (5)
      • ►  Dec 28 (1)
      • ►  Dec 25 (1)
      • ►  Dec 24 (1)
      • ►  Dec 22 (2)
    • ►  November 2022 (2)
      • ►  Nov 10 (2)
    • ►  October 2022 (1)
      • ►  Oct 05 (1)
    • ►  August 2022 (2)
      • ►  Aug 28 (1)
      • ►  Aug 27 (1)
    • ►  June 2022 (2)
      • ►  Jun 27 (1)
      • ►  Jun 26 (1)
    • ►  May 2022 (1)
      • ►  May 08 (1)
    • ►  April 2022 (2)
      • ►  Apr 19 (1)
      • ►  Apr 11 (1)
    • ►  March 2022 (30)
      • ►  Mar 09 (6)
      • ►  Mar 08 (22)
      • ►  Mar 07 (2)
    • ►  February 2022 (5)
      • ►  Feb 16 (3)
      • ►  Feb 15 (1)
      • ►  Feb 12 (1)
    • ►  January 2022 (17)
      • ►  Jan 31 (1)
      • ►  Jan 30 (2)
      • ►  Jan 25 (1)
      • ►  Jan 12 (1)
      • ►  Jan 11 (1)
      • ►  Jan 07 (3)
      • ►  Jan 03 (6)
      • ►  Jan 02 (2)
  • ►  2021 (111)
    • ►  December 2021 (63)
      • ►  Dec 26 (2)
      • ►  Dec 23 (1)
      • ►  Dec 21 (1)
      • ►  Dec 20 (1)
      • ►  Dec 14 (1)
      • ►  Dec 13 (2)
      • ►  Dec 12 (49)
      • ►  Dec 11 (1)
      • ►  Dec 07 (3)
      • ►  Dec 06 (2)
    • ►  November 2021 (6)
      • ►  Nov 26 (2)
      • ►  Nov 09 (1)
      • ►  Nov 07 (1)
      • ►  Nov 06 (1)
      • ►  Nov 01 (1)
    • ►  October 2021 (3)
      • ►  Oct 19 (1)
      • ►  Oct 14 (1)
      • ►  Oct 06 (1)
    • ►  September 2021 (10)
      • ►  Sep 28 (2)
      • ►  Sep 19 (4)
      • ►  Sep 07 (2)
      • ►  Sep 02 (1)
      • ►  Sep 01 (1)
    • ►  August 2021 (4)
      • ►  Aug 27 (1)
      • ►  Aug 24 (1)
      • ►  Aug 23 (1)
      • ►  Aug 16 (1)
    • ►  July 2021 (6)
      • ►  Jul 28 (1)
      • ►  Jul 17 (3)
      • ►  Jul 07 (1)
      • ►  Jul 03 (1)
    • ►  June 2021 (1)
      • ►  Jun 30 (1)
    • ►  May 2021 (2)
      • ►  May 20 (1)
      • ►  May 19 (1)
    • ►  April 2021 (4)
      • ►  Apr 06 (3)
      • ►  Apr 05 (1)
    • ►  March 2021 (9)
      • ►  Mar 30 (2)
      • ►  Mar 26 (1)
      • ►  Mar 15 (1)
      • ►  Mar 14 (2)
      • ►  Mar 11 (1)
      • ►  Mar 10 (1)
      • ►  Mar 09 (1)
    • ►  January 2021 (3)
      • ►  Jan 13 (3)
  • ►  2020 (107)
    • ►  December 2020 (8)
      • ►  Dec 28 (2)
      • ►  Dec 27 (1)
      • ►  Dec 23 (1)
      • ►  Dec 22 (1)
      • ►  Dec 15 (1)
      • ►  Dec 07 (1)
      • ►  Dec 06 (1)
    • ►  November 2020 (17)
      • ►  Nov 28 (1)
      • ►  Nov 25 (1)
      • ►  Nov 24 (1)
      • ►  Nov 22 (2)
      • ►  Nov 17 (5)
      • ►  Nov 14 (2)
      • ►  Nov 12 (1)
      • ►  Nov 10 (1)
      • ►  Nov 08 (1)
      • ►  Nov 03 (2)
    • ►  October 2020 (16)
      • ►  Oct 31 (1)
      • ►  Oct 28 (1)
      • ►  Oct 27 (1)
      • ►  Oct 18 (6)
      • ►  Oct 13 (2)
      • ►  Oct 11 (1)
      • ►  Oct 05 (2)
      • ►  Oct 04 (1)
      • ►  Oct 03 (1)
    • ►  September 2020 (23)
      • ►  Sep 29 (9)
      • ►  Sep 28 (2)
      • ►  Sep 27 (4)
      • ►  Sep 08 (1)
      • ►  Sep 06 (3)
      • ►  Sep 03 (3)
      • ►  Sep 01 (1)
    • ►  August 2020 (1)
      • ►  Aug 26 (1)
    • ►  June 2020 (1)
      • ►  Jun 13 (1)
    • ►  May 2020 (3)
      • ►  May 19 (1)
      • ►  May 14 (1)
      • ►  May 07 (1)
    • ►  April 2020 (2)
      • ►  Apr 23 (1)
      • ►  Apr 22 (1)
    • ►  March 2020 (13)
      • ►  Mar 20 (1)
      • ►  Mar 19 (2)
      • ►  Mar 17 (1)
      • ►  Mar 10 (5)
      • ►  Mar 09 (1)
      • ►  Mar 04 (1)
      • ►  Mar 01 (2)
    • ►  February 2020 (18)
      • ►  Feb 29 (1)
      • ►  Feb 26 (1)
      • ►  Feb 25 (2)
      • ►  Feb 22 (1)
      • ►  Feb 16 (4)
      • ►  Feb 12 (3)
      • ►  Feb 10 (1)
      • ►  Feb 08 (1)
      • ►  Feb 07 (3)
      • ►  Feb 04 (1)
    • ►  January 2020 (5)
      • ►  Jan 20 (1)
      • ►  Jan 19 (2)
      • ►  Jan 13 (1)
      • ►  Jan 08 (1)
  • ▼  2019 (121)
    • ►  December 2019 (10)
      • ►  Dec 27 (2)
      • ►  Dec 22 (1)
      • ►  Dec 21 (1)
      • ►  Dec 15 (1)
      • ►  Dec 14 (1)
      • ►  Dec 07 (1)
      • ►  Dec 06 (1)
      • ►  Dec 05 (1)
      • ►  Dec 01 (1)
    • ►  November 2019 (45)
      • ►  Nov 25 (3)
      • ►  Nov 24 (1)
      • ►  Nov 20 (10)
      • ►  Nov 18 (12)
      • ►  Nov 17 (4)
      • ►  Nov 16 (2)
      • ►  Nov 12 (2)
      • ►  Nov 11 (1)
      • ►  Nov 06 (2)
      • ►  Nov 05 (1)
      • ►  Nov 04 (4)
      • ►  Nov 03 (2)
      • ►  Nov 02 (1)
    • ▼  October 2019 (4)
      • ►  Oct 28 (1)
      • ►  Oct 25 (1)
      • ▼  Oct 01 (2)
        • OOP : Dependency Injection in PHP
        • OOP : Abstract Class vs Interface
    • ►  September 2019 (12)
      • ►  Sep 30 (1)
      • ►  Sep 26 (2)
      • ►  Sep 18 (1)
      • ►  Sep 17 (3)
      • ►  Sep 11 (2)
      • ►  Sep 09 (1)
      • ►  Sep 03 (2)
    • ►  August 2019 (1)
      • ►  Aug 30 (1)
    • ►  July 2019 (12)
      • ►  Jul 29 (2)
      • ►  Jul 16 (2)
      • ►  Jul 11 (1)
      • ►  Jul 10 (2)
      • ►  Jul 06 (2)
      • ►  Jul 03 (2)
      • ►  Jul 02 (1)
    • ►  May 2019 (22)
      • ►  May 28 (2)
      • ►  May 27 (2)
      • ►  May 24 (1)
      • ►  May 23 (1)
      • ►  May 22 (1)
      • ►  May 19 (2)
      • ►  May 18 (4)
      • ►  May 16 (1)
      • ►  May 15 (1)
      • ►  May 13 (1)
      • ►  May 05 (2)
      • ►  May 03 (4)
    • ►  April 2019 (9)
      • ►  Apr 29 (1)
      • ►  Apr 21 (5)
      • ►  Apr 08 (2)
      • ►  Apr 03 (1)
    • ►  March 2019 (1)
      • ►  Mar 07 (1)
    • ►  February 2019 (2)
      • ►  Feb 17 (1)
      • ►  Feb 10 (1)
    • ►  January 2019 (3)
      • ►  Jan 16 (1)
      • ►  Jan 12 (1)
      • ►  Jan 07 (1)
  • ►  2018 (66)
    • ►  December 2018 (9)
      • ►  Dec 24 (1)
      • ►  Dec 19 (1)
      • ►  Dec 17 (3)
      • ►  Dec 16 (1)
      • ►  Dec 12 (1)
      • ►  Dec 05 (1)
      • ►  Dec 04 (1)
    • ►  November 2018 (20)
      • ►  Nov 28 (1)
      • ►  Nov 26 (1)
      • ►  Nov 21 (2)
      • ►  Nov 19 (1)
      • ►  Nov 17 (1)
      • ►  Nov 14 (3)
      • ►  Nov 12 (1)
      • ►  Nov 08 (7)
      • ►  Nov 07 (3)
    • ►  October 2018 (37)
      • ►  Oct 31 (3)
      • ►  Oct 30 (3)
      • ►  Oct 29 (1)
      • ►  Oct 28 (6)
      • ►  Oct 24 (2)
      • ►  Oct 23 (15)
      • ►  Oct 21 (1)
      • ►  Oct 18 (2)
      • ►  Oct 03 (4)
  • ►  2017 (11)
    • ►  December 2017 (1)
      • ►  Dec 20 (1)
    • ►  November 2017 (1)
      • ►  Nov 29 (1)
    • ►  September 2017 (3)
      • ►  Sep 24 (1)
      • ►  Sep 21 (2)
    • ►  August 2017 (6)
      • ►  Aug 27 (2)
      • ►  Aug 22 (2)
      • ►  Aug 21 (1)
      • ►  Aug 20 (1)

Report Abuse

Search This Blog

About Me

  • হোম
  • My Website
Simple theme. Theme images by sndr. Powered by Blogger.