SOLID Design Principles – Part 4

SOLID Design principles

In this series we are looking at the SOLID design principles. In this week’s tip, we’ll cover the fourth principle in the acronym: the Interface Segregation principle (LSP).

If you missed the previous posts in this sequence, you can find them here:

The Interface Segregation Principle

“Many small specific interfaces are better than one large general-purpose interface”.

The dependency of one class to another should depend on the smallest possible interface. The purpose is to make clients use the smallest and most cohesive interface as possible.

Most clients of a class only need a small number of the methods of an interface. To facilitate this, we should create separate, focused interfaces, each of which serves a specific purpose. A class will then implement only the interfaces it needs, as opposed to having to implement many methods that it doesn’t need. Implementing a large general-purpose interface often leads to accidental close class couplings and additional dependencies.

Extending the ISP to its logical conclusion, the most specific and focused interface will consist of only one single method to implement. This is often called a SAM (Single Abstract Method) interface. In the Java API, it is now called a functional interface. A functional interface has a number of additional uses with lambdas in functional programming. See my previous blog post on functional interfaces for more information.

Java API Examples

There are a large number of obvious examples of the ISP in the standard Java API. Just in the java.lang package we can find the AppendableAutoCloseableCharSequenceComparableIterableReadable  and  Runnable interfaces. All of these interfaces have very focused, narrow uses. Most are functional interfaces containing only a single abstract method.

Two of the previous interfaces are not functional interfaces, but they are still highly focused on specific use cases. Appendable has three overloaded append() methods to append characters to the end of other objects. CharSequence defines six common methods that provide read-only access to objects that contain sequences of chars.

Custom Interface Example

Let’s look at how we would implement ISP in our own design. Let’s say we’re modelling an Employee class and a Contractor class. Obviously they have similar fields: namesurnamedateOfBirthgender, etc. They have differences too: employees receive a salary, while contractors have an hourly rate that determines their remuneration. They have similar methods: work()doTimeSheets()drinkCoffee(), etc. The common fields and methods can be factored out and moved to a Person base class. The work() and doTimeSheets() methods obviously can’t be moved to the Person class because they have nothing to do with a Person.

A good idea would be to move the work() and doTimeSheets() methods into an interface that would be appropriately implemented in the Employee and Contractor classes.

public interface Productive {
   public void work();
   public void doTimeSheets();
}

We should debate whether doing time sheets is actually being productive, and whether the method belongs in the Productive interface. Let’s think of a Gardener or a Guard class that inherits from Employee and implements Productive. No one can argue that they work, but no one would expect them to fill in time sheets.

We might also want to model a GuardDog to patrol with the Guard. Again, no one would argue with the fact that the GuardDog is a working dog, and should have a work() method. And very importantly , a GuardDog would never fill in a time sheet!

Obviously putting the doTimeSheets() method in the Productive interface limits its use, and doesn’t model Productive objects correctly. Doing so could lead to unintended class dependencies.

What now? ISP to the rescue! Let’s apply the Interface Segregation principle to our current design and split the Productive interface into two focused interfaces, each with their own use cases:

public interface Productive {
    public void work();
}

public interface CompleteAdminstrativeTasks {
    public void doTimeSheets();
}

This would lead to code which follows good design principles by conforming to the ISP:

public class Employee extends Person implements Productive, CompleteAdminstrativeTasks {

    public void work() {}
        // appropriate code here...
    }

    public void doTimeSheets() {}
        // appropriate code here...
    }
}

public class GuardDog extends Dog implements Productive {

    public void work() {}
        // appropriate code here...
    }
}

What’s next?

Next week we’ll look at the final principle in the acronym: the Dependency Inversion Principle (DIP).

As always, please share your comments and questions.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.