So, a little while ago, when I’ve dug in deep into the sea of OOP, a popular new technique surfaced and its called Dependency Injection. Take note here, that I’m not rediscovering the wheel here. Just pointing the obvious and explaining it – My Way!

There are literally thousands of information about that topic, but I’ll try to be brief here, take a little as much I can of your time, and give you all the info, so when you set sail in the vast sea of Information Technologies, you don’t like your support.

Let us begin.

 Dependency Injection Series

The DI topic emerged about 7 or 8 years ago, and expanded its popularity rapidly. The concept is very simple and follows the S.O.L.I.D principle (more on that).

DI is part of the PHP best practices (not only in PHP but in general), but let us take a note here and say this: “No one will force you to obey a recommendation, or use this technique, or this design pattern”. But complying with the given recommendation, or pattern will result in a better software architecture, better scalability of the application, code re-usability, etc.

Source in Bulgarian

The concept is a lot simpler. Maybe frightening at first, I’ll try to make it a little clearer. DI allows classes not to depend on own dependencies but to take it for granted. Of course there are people that don’t like this pattern, and you can find a lot of hate. As stated above, if you wish not to use it – that is OK.

Let us define it: Dependency Injection means that if a class needs an object or configuration, we force that information to be passed into that class.

Follow the next explanation, and all examples are artificially simplified here.


class Payment
{
    public function __construct()
    {
        $this->gateway = new PayPal();
        $this->db = new MySQLDB();
        $this->invoice = new Invoice();
    }
}

Lets say we have a class Payment that executes a payment process via PayPal. We want to log this process in the database, and issue an invoice for the user. And in DI fashion – we have an object that depends on other outside resources.

The problem here is that the Payment class creates dependencies on its own, and the dependencies (the other classes) can have dependencies on their own. This isn’t a good practice for number of reasons. In this case we’ve created a tight coupling … Payment has to access the other classes namespaces, and their current implementation. The Payment class can be used only in this tightly coupled environment, if we try to export it as a library we’ll have to implement all the other classes with him.

As you can imagine this is unacceptable for the reason stated in S.O.L.I.D.’s S part:

Single responsibility – a class must have only one responsibility. And that way you violate that single responsibility, because you need to take care of the PayPal object two. The DB with its own configuration. The Invoice class logic. You get the point.

So what happens if we try to scale that Payment class and add another payment processor like Stripe? We save all the class logic except that in the __construct. One implementation is extending a given class, using the magic __setter and __getter or reflections and … I just got lost. The other option is this “classic”:


class Payment
{
    public function __construct($gateway)
    {
        if ($gateway == "PayPal") {
            $this->gateway = new PayPal();
        } else {
            $this->gateway = new Stripe();
        }
        $this->db      = new MySQLDB();
        $this->invoice = new Invoice();
    }
}

At first the problem has it’s solution, right? On the contrary, we’ve made an even worse situation, and we suffer all shortcomings of the previous implementation, but we’ve created even more dependencies. Try to imagine if we need to expand even further – adding a new payment processor. An object constructor with many many if / else blocks is a nightmare for testing, supporting or extending.

The Dependency Injection

OK. So the problem is that we use new Object inside the constructor. What will happen if we pass the dependencies from the outside and add them as properties of that object?


class Payment
{
    public function __construct($gateway, $db, $invoice)
    {
        $this->gataway = $gateway;
        $this->db      = $db;
        $this->invoice = $invoice;
    }
}

So is this the right solution? Looks good, right – our class waits for its dependencies like a good boy, but this implementation is transferring the complexity from one point to another. So before this the complexity was inside our object, and now the other that calls and pass the dependencies into this object must take care of them.

The code above has another problem, this is a problem with all script languages like PHP. The class doesn’t depend on the type of the variable or object passed into the function or method.

What is the meaning here? Why are you pointing this out? Simple. Every variable injected inside the constructor in the figure above can be anything. If we pass a string for the $db, what will happen? An error. Right! So particular in PHP this has the next solution.

class Payment
{
    public function __construct(PayPal $gateway, MySQLDB $db, Invoice $invoice)
    {
        $this->gateway = $gateway;
        $this->db      = $db;
        $this->invoice = $invoice;
    }
}

Using the typecasting we ensure passing specific classes, and we cannot parse string or anything else into the constructor. But still, we haven’t found a solution for this particular problem – what will happen if we want to use different payment processor such as Stripe? We cannot deliver a different type, because the object expects a PayPal instance. The one option that we have here is OK, but using it in general is avoided, having a base class:

class PaymentProcessor
{
    public function pay()
    {}
}

class PayPal extends PaymentProcessor
{}

class Stripe extends PaymentProcessor
{}

class Payment
{
    public function __construct(PaymentProcessor $gateway, MySQLDB $db, Invoice $invoice)
    {
        $this->gateway = $gateway;
        $this->db      = $db;
        $this->invoice = $invoice;
    }
}

Note here, that Payment class expects the PaymentProcessor. In this way if you want to make another payment processor class, you just extend the PaymentProcessor and it will work, because the method pay() is guaranteed to exist.

So we’ve grown. The dependencies are removed from the object, and we’ve ensured extended functionality via the Base class. But this is not the ideal solution – yeah it works, but we still have a dependency through the base class – PaymentProcessor with his dependencies. In general the idea here is – the smallest the number of dependencies, the better. The main problem here is that if a system is complex (bigger than usual) these little dependencies tend to escalate to a big complexity.

The interface way

Lets think about the Payment class, and we focus onto the payment processor.

We need the injected instance to be serialised in exact defined methods, so we can guarantee, when Payment calls the pay() method this method will exist. How can we achieve that without using base or abstract classes? Simple – interface.

As I’ve stated in this post here – an interface is like a contract. They cannot be instanced, you cannot extend them, they force the class that implements them to have the methods that are described in the interface. It is a lot simpler that it sounds 😉

interface PaymentProcessor
{
    public function pay();
}

class PayPal implements PaymentProcessor
{}

class Stripe implements PaymentProcessor
{}

class Payment
{

    public function __construct(PaymentProcessor $gateway, MySQLDB $db, Invoice $invoice)
    {
        $this->gateway = $gateway;
        $this->db      = $db;
        $this->invoice = $invoice;
    }
}

Examine the example carefully, the differences between the one above and the upper are little but essential. The creation of an interface PaymentProcessor that ensures every class implementing it will have the pay() method. Then we tell the Payment class to accept objects that implement that interface. This way we guarantee, that the object passed has the pay() method and it won’t raise MethodNotFound or something else.

The biggest advantage coding with interfaces is that we are not dependent via the implementation. When we use base or abstract class we obligate all to considerate it. And that is not needed. The only thing that Payment needs to care about, is an object with the pay() method. What is the pay() implementation – does not matter.

Now, if we wish to extract this class into a library it will be easy, and we will have to include the interfaces but no specific implementation. This is the base idea behind the Dependency Injection pattern / technique or the IoC (Inversion of control). The same thing just some difference with the semantics.

Part 2

Published by T3at1m3

PHP Developer

Join the Conversation

2 Comments

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.