Skip links

SOLID - The D is for Dependency Inversion Principle

02 January 2013 10:07 - by Freek Lijten - 0 comments

Tags: , , ,

The fifth principle of the five SOLID principles is the Dependency Inversion Principle (DIP). You might expect an article on the ISP first, but I feel the internet is filled with enough posts about SOLID. This one was already finished so I'll publish it nonetheless :) 

The DIP deals with avoiding mixing different levels of abstraction in your code. In this article we will explore the last of these principles in depth.

As for the previous four, Robert C. Martin took the time to summarize the Dependency Inversion Principle for us:

1. High level modules should not depend upon low level modules. Both should depend upon abstractions.

2. Abstractions should not depend upon details. Details should depend upon abstractions.

Let's look at an example to clarify this:

function storeBike()
{
    $Bike = getBikeInformationFromPostRequest();
    writeBikeToMySQLi($Bike);
}

function getBikeInformationFromPostRequest()
{
    //parse $_POST and return a Bike object
}

function writeBikeToMySQLi(Bike $Bike)
{
    //store $Bike in a new MySQLi record
}

Of course this code is extremely trivial but we have managed to create a two line function (storeBike) with an algorithm that is absolutely not reusable. Why is it not reusable? Because a high level action (store a Bike) depends upon two low level actions: parse a request into bike information and save bike information to MySQL.

If you want to read from something else than $_POST or save to something else than MySQL you have to rewrite the algorithm. Although our algorithm is small and trivial it would be 100% reusable in other cases if the code was refactored to this:

interface BikeReader
{
    public function read();
}

interface BikeWriter()
{
    public function write(Bike $Bike);
}

function storeBike(BikeReader $Reader, BikeWriter $Writer)
{
    $Bike = $Reader->read();
    $Writer->write($Bike);
}

The new code works becuase the high level store function now only depends on abstractions. The same function could be used for reading bikes from a file and write them to output as long as the used BikeReader and BikeWriter implement the interfaces.

A more tangible example

I want to show you how depending on abstractions makes our lives easier in a larger example. Since we're going to be loading, updating and storing bikes a lot in our application I would like an Api for that. It would not be such a good idea to write the same queries or perform the same validation over and over.

In the following example there are two interfaces, one for bike and one for a repository. These are the abstractions details can depend on. Of course we need a detail as well, this will be our Api. This Api is where the logic is! Validation, logging, event triggering, you name it. This is what desperately needs to be tested in every application out there!

Lets have a look at our first interface:

interface Bike
{
    /**
     * @return int
     */
    public function getId();

    /**
     * @return string 
     */
    public function getName();

    /**
     * @param int $id
     */
    public function setId($id);

    /**
     * @param string $name
     */
    public function setName($name)

}

The second interface we need is for a class that is able to perform CRUD-like operations on bikes, a repository:

interface BikeRepository
{
    /**
     * @return int 
     */
    public function create();

    /**
     * @param int $bikeId
     * @return Bike
     */
    public function retrieve($bikeId)

    /**
     * @param Bike $Bike
     * @return bool
     */
    public function update(Bike $Bike);

    /**
     * @param Bike $Bike
     * @return bool
     */
    public function delete(Bike $Bike);
}

Now that we have defined our abstractions we can have a look at our detail. Again, this is where the logic is! For readability I only show a possible update function:

class BikeApi
{
    private $Repository;

    public function __construct(BikeRepository $Repository)
    {
        $this->Repository = $Repository;
    }

    /**
     * @param Bike $Bike
     * @return bool
     */
    public function update(Bike $Bike)
    {
        $name = $Bike->getName();
        $id = $Bike->getId();

        if (empty($name) || empty($id)) {
            return false;
        }

        if ( ! $this->BikeRepository->update($Bike) ) {
            return false;
        }
        
        return true;
    }
}

What did we gain?

Now why did we go to all that trouble of defining interfaces and injecting objects into our BikeApi?

It allows us to change implementation details of one detail without affecting another detail.

The repository that is used by the Bike Api can store the Bike data however it wants to. The business logic is completely detached from the implementation detail of our data storage. Changing the way we store data is a relatively small problem that has no effect outside repositories. As long as the new solution implements the existing interface we even know for sure that our application will keep working (tests remember!).

Confusion with dependency injection

Dependency inversion is often confused with dependency injection. While they touch similar grounds they are not the same. Dependency inversion is described in the two rules at the very start of this article. Dependency injection is a means to an end.

We enable dependency inversion by injecting the Repository into the constructor instead of calling new SomeImplementationOfRepository() inside the Api. With the "new call" we tie the Api to a specific implementation, with the typehinted constructor injection we don't.

This brings a second advantage. Since the repository is injected and the interface is type hinted we can replace it with a mock object based on that interface in our tests. This allows us to test the business logic in complete seperation of the rest of the system, which is all we really want to. You can see how easy testing the Api in isolation of the repository is in the two sample tests for the Api below:

class BikeApiTest
{
    public function testUpdate_whenBikeNameIsNotSet_shouldReturnFalse()
    {
        $RepositoryStub = $this
            ->getMockBuilder('BikeRepository')
            ->getMock();
        
        $BikeStub = $this
            ->getMockBuilder('Bike')
            ->getMock();
        $BikeStub
            ->expects($this->any())  
            ->method('getName')
            ->will($this->returnValue(false));

        $BikeApi = new BikeApi($RepositoryStub);
        $result = $BikeApi->update($BikeStub);

        $this->assertFalse($result); 
    }

    public function testUpdate_whenBikeRepositoryUpdateFails_shouldReturnFalse()
    {
        $RepositoryStub = $this
            ->getMockBuilder('BikeRepository')
            ->getMock();
        $RepositoryStub = $this
            ->expects($this->any())
            ->method('update')
            ->will($this->returnValue(false));
        
        // Make sure the update does not return false to early by setting up a
        // Bike that does return values for name and id.
        $BikeStub = $this
            ->getMockBuilder('Bike')
            ->getMock();
        $BikeStub
            ->expects($this->any())  
            ->method('getName')
            ->will($this->returnValue('name'));
        $BikeStub
            ->expects($this->any())  
            ->method('getId')
            ->will($this->returnValue('id'));

        $BikeApi = new BikeApi($RepositoryStub);
        $result = $BikeApi->update($BikeStub);

        $this->assertFalse($result); 
    }
}

Conclusion

By separating details from other details and writing software that only knows about other parts through interfaces we can create software that enables us to change implementation of parts without breaking or changing other parts. As an added bonus, this decoupling of parts allows us to unit test our code. Don't be afraid of code that others might find java-esque if it makes refactoring easier and your test suite larger!

Share this post!

Comments

Leave a comment!

Italic and bold

*This is italic*, and _so is this_.
**This is bold**, and __so is this__.

Links

This is a link to [Procurios](http://www.procurios.nl).

Lists

A bulleted list can be made with:
- Minus-signs,
+ Add-signs,
* Or an asterisk.

A numbered list can be made with:
1. List item number 1.
2. List item number 2.

Quote

The text below creates a quote:
> This is the first line.
> This is the second line.

Code

A text block with code can be created. Prefix a line with four spaces and a code-block will be made.