DesignPatternsPHP / DesignPatternsPHP

Sample code for several design patterns in PHP 8.x

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2.11.3 recommends using DI instead, not being specific

volk-s opened this issue · comments

please excuse my noob question
how would the example of 2.11.3 and 2.11.4 look like, if it would have been done with DI as suggested?

The concept behind Dependency Injection is that everything is loosely coupled and everything can be replaced easily to introduce new behaviors.

Let's say I have a service that says "Good morning" if it's the morning, "Good afternoon" in the afternoon and "Good evening" in the evening:

<?php
class WelcomeMessage
{
    public static function GetMessage()
    {
        $current_hour = (int)date('H');

        if ($current_hour < 12)
        {
            return "Good evening";
        }
        else if ($current_hour < 17)
        {
            return "Good afternoon";
        }
        else
        {
            return "Good evening";
        }
    }
}

print(WelcomeMessage::GetMessage());

Now let's say you had to test your service to make sure it works properly, how would you write a test that asserts that the right message is sent in the afternoon? You could write a test that waits until it is the afternoon to run it, but it would be really inconvenient. This is where DI comes in. You can create a way to inject a dependency and change the behavior of your program, this way, you don't need to change your code to test it. We can do that by abstracting the date() dependency into an abstract class.

I rewrote the class using dependency injection:

<?php

abstract class AbstractDateTimeProvider
{
    abstract public function getCurrentHour();
}

class DateTimeProvider extends AbstractDateTimeProvider
{
    public function getCurrentHour()
    {
        return (int) date('H');
    }
}

class DateTimeProviderMock extends AbstractDateTimeProvider
{
    public function getCurrentHour()
    {
        return 5;
    }
}

class WelcomeMessage
{
    private $dateTimeProvider;
  
    public function __construct($dateTimeProvider)
    {
        $this->dateTimeProvider = $dateTimeProvider;
    }

    public function getMessage()
    {
        $current_hour = $this->dateTimeProvider->getCurrentHour();

        if ($current_hour < 12)
        {
            return "Good evening";
        }
        else if ($current_hour < 17)
        {
            return "Good afternoon";
        }
        else
        {
            return "Good evening";
        }
    }
}

// New way to use the class
$dateTimeProvider = new DateTimeProvider();
$welcomeMessage = new WelcomeMessage($dateTimeProvider);

print($welcomeMessage->getMessage());


// We inject a fake time (5 AM) to test that the right message is sent
$dateTimeProviderMock = new DateTimeProviderMock();
$welcomeMessage = new WelcomeMessage($dateTimeProviderMock);

print($welcomeMessage->getMessage());

Now you can create an instance of DateTimeProviderMock or DateTimeProvider to either to mock the dependency and inject a fake time or use the class the normal way and we didn't have to change anything else to the the class. It is now loosely coupled with its dependency.

I hope this answers your question.