PHP Course : SOLID - Interface Segregation

profile picture

PHP Course : SOLID - Interface Segregation

"Interface Segregation"... what the hell does that mean now... It seems like all those principles were named to bring more confusion than clarity right ? Yet they are very easy to understand once you look at the right example.

This one in particular sounds confusing but is fairly easy to understand. Let's dive right in.

Definition

This principle states that a client should not be forced to implement an interface that it doesn't use.

Can't really make it easier than that.

Like all the other principles, it all comes back to the idea of knowledge. The knowledge that an object has of another object.

Examples

Violation of the Principle

Imagine this simple code: A ProjectManager object that can manage() a Developer

class ProjectManager
{
    public function manage(Developer $developer)
    {
        $developer->code();
        $developer->test();
    }
}

class Developer
{
    public function code()
    {
        // code...
    }

    public function test()
    {
        // code...
    }
}

Here, ProjectManager has knowledge of Developer, meaning that ProjectManager depends on Developer. And if ProjectManager depends on Developer, then ProjectManager depends on whatever Developer depends as well.

As you can imagine, that may not be ideal. There is too much shared knowledge here.

Now what if the ProjectManager hires a Tester ? A Tester doesn't code() and yet they are required to deliver the project.

Yeah, we know what to do, right ? We just need to setup an interface!

interface WorkerInterface
{
    public function code();

    public function test();
}

class Developer implements WorkerInterface
{
    public function code()
    {
        return 'coding';
    }

    public function test()
    {
        return 'basic testing';
    }
}

class Tester implements WorkerInterface
{
    public function code()
    {
        return null;
    }

    public function test()
    {
        return 'advanced testing';
    }
}

But mmmh, this looks weird doesn't it ? A Tester doesn't code, so we have to return null. And this is bad.

Well this is what Interface Segregation is all about.

Client should not be forced to implement interface they don't use. And right now, Tester is being forced to implement the code method even though there is no use for it. This violates the Interface Segregation Principle.

Solution

How can we solve that? We see that the issue comes from the WorkerInterface which force its subclasses to implements both methods.

The solution is quite easy, we just need to split this interface into smaller chunks. Even an interface with a single method is perfectly fine. You should worry about interface that are too big (fat interfaces), not too small.

By splitting up the WorkerInterface we have something like this:

interface CodeableInterface
{
    public function code();
}

interface TestableInterface
{
    public function test();
}

class Developer implements CodeableInterface, TestableInterface
{
    public function code()
    {
        return 'coding';
    }

    public function test()
    {
        return 'basic testing';
    }
}

class Tester implements TestableInterface
{
    public function test()
    {
        return 'advanced testing';
    }
}

But what about the ProjectManager class then?? This one is not interested anymore in Developer or Tester, but only in CodeableInterface and TestableInterface, right?

class ProjectManager
{
    public function manage(TestableInterface $worker)
    {
        if ($worker instanceof Developer) { // breaks the Open-Closed Principle!
            $worker->code();
        }

        $worker->test();
    }
}

This problem sounds familiar ? This is because we discussed it in the Open-Closed Principle.

If you remember correctly this lesson, you should know how to fix this problem.

We need another Interface! Yes, because the ProjectManager doesn't need to know if the worker is a Tester or a Developer(at least in this example), he just wants to have to work done! He's only interested in Managable classes... See where this is going ? πŸ˜‰

class ProjectManager
{
    public function manage(ManagableInterface $worker)
    {
        $worker->work();
    }
}

interface ManagableInterface
{
    public function work();
}

interface CodeableInterface
{
    public function code();
}

interface TestableInterface
{
    public function test();
}

class Developer implements CodeableInterface, TestableInterface, ManagableInterface
{
    public function work()
    {
        $this->code();
        $this->test();
    }

    public function code()
    {
        return 'coding';
    }

    public function test()
    {
        return 'basic testing';
    }
}

class Tester implements TestableInterface, ManagableInterface
{
    public function work()
    {
        $this->test();
    }

    public function test()
    {
        return 'advanced testing';
    }
}

Hopefully, this makes sense πŸ˜…

See how everything is cleaner ? No conditions whatsoever.

Conclusion

I'm just going to repeat myself, but with the above example it should be cristal clear:

A client should not be forced to implement an interface that it doesn't use.

lesson
php course
solid
interface
segregation

2019 My Dynamic Production SPRL All rights Reserved.