SOLID: The Interface Segregation Principle (ISP)

 Uncle Bob describes it as:

"The ISP acknowledges that there are objects that require non-cohesive interfaces; however it suggests that clients should not know about them as a single class. Instead, clients should know about abstract base classes that have cohesive interfaces."

After reading several documents and watching endless videos on this subject what I understand is that interface clients should not be forced to depend on methods that it doesn't use (giving those methods a null value).  Or in other words, it makes no sense to force a client to have a list of methods which return false, null or throw exceptions. Let's have a look at an example


<?php

// Interface Segregation Principle Violation
interface HospitalFunctions
{
    public function canGiveDiagnose();
    public function diagnose();
    public function administrateIVMedicine();
}

class Doctor implements HospitalFunctions
{

    public function canGiveDiagnose()
    {
        return true;
    }

    public function diagnose()
    {
        return "Checking on your symptoms to give you a diagnose";
    }

    public function administrateIVMedicine()
    {
        return "Please, roll up your sleeves for the administration of IV drugs";
    }
}

class Nurse implements HospitalFunctions
{

    public function canGiveDiagnose()
    {
        return false;
    }

    public function diagnose()
    {
        throw new Exception("Sorry, I am not authorized to give a diagnose");
    }

    public function administrateIVMedicine()
    {
        return "Please, roll up your sleeves for the administration of IV drugs";
    }
}

class Hospital
{
    public function treatPatient(HospitalFunctions $healthCareStaff)
    {
        if ($healthCareStaff->canGiveDiagnose()) {
            $healthCareStaff->diagnose();
        }
        $healthCareStaff->administrateIVMedicine();
    }
}


If we have a look at the example, we realize that Nurse class doesn't really need doctors functions, which causes two out of three methods implemented by the interface to return a false value and throw an exception. It makes more sense to segregate the interfaces as follows:

 

<?php
//Refactoring
interface DoctorsTasks
{
    public function diagnose();
}

interface NursesTasks
{
    public function administrateIVMedicine();
}

class Doctor implements DoctorsTasks, NursesTasks
{

    public function diagnose()
    {
        return "Checking on your symptoms to give you a diagnose";
    }

    public function administrateIVMedicine()
    {
        return "Please, roll up your sleeves for the administration of IV drugs";
    }
}

class Nurse implements NursesTasks
{
    public function administrateIVMedicine()
    {
        return "Please, roll up your sleeves for the administration of IV drugs";
    }
}

class PatientTreatment
{
    public function giveDiagnose(DoctorsTasks $doctor)
    {
        $doctor->diagnose();
    }
    public function administrateIVDrugs(NursesTasks $nurse) {
        $nurse->administrateIVMedicine();
    }
}

As you can see through the example, as simple as it might seem, Interfaces belong to clients, and therefore, Interfaces should be defined based on the clients using them instead of basing them on future implementations.
We should avoid the use of header interfaces, which are basically a collection of all of the clients methods headers. Instead, we should first define each use case, and base on that our interfaces. 

Thinking about another example to represent this principle, imagine I want to send a notification through email, Facebook or sms everytime I post in my blog. Which of the following signatures do you think would be more suitable for a $Notifier interface?

  1. $Notifier ($Content)
  2. $Notifier ($ReceiverEmail, $EmailSubject, $EmailContent)
  3. $Notifier ($DestinationNumber, $Subject, $Content)
  4. $Notifier ($FacebookGroup, $PostContent)
  5. $Notifier ($Destination, $Subject, $Content)
Option number 2 would be too specific for email notifications, number 3 would be too specific for sms notifications while number 4 would be more specific for facebook posts. Number 5 might seem a good abstraction attempt, but it does involve issues regarding types as $Destination could be a telephone number, an email or a facebook group.
Option number 1 could be a good option since the specifics of each of the implementations can be included in the constructors instead of the interface. 
But ideally we should have something like this:

Post Published triggers:
  • SMS Notifier Event
  • Email Notifier Event
  • Facebook Notifier Event
I hope you found this post useful and easy to understand. As usual, keep reading, keep coding, keep enjoying it. 
Happy Tuesday!

Comments

Popular posts from this blog

SOLID: The Single-Responsibility Principle (SRP)

Symfony HTTP client

Inheritance: Abstract classes and interfaces