Interface segregation principle
This is part four of a five part series covering the SOLID principles. For more information on what the SOLID principles are, check out part one.
- Single responsibility principle
- Open/closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
No client should be forced to depend on methods it does not use.
What does it mean?
This principle is one of the more clear ones but lets define the terminology in case it's unfamiliar. A client is something that is asking/requesting something from another class. A method is a function that is on a class. It differs from a function as it can access data on the class.
This principle is concerned with how we use classes. It helps us to separate our code and improve our developing experience over a long period. This principle differs from the single responsibility principle which is concerned with what a group of codes responsibility is. Whereas interface segregation is about how we use and interact with the code.
Here we have a class called
Car with a list of methods on it, such as
doors etc. We are creating an instance of it and storing it in a variable called
electricCar. We are calling the
information method on it to print out the cars information to the webpage.
This works great, you can create as many cars as you like and print the information to the webpage. Now pretend you're given a new task of creating a
motorCar. You reach for the
Car class, initialise it and store the result in a variable called
motorCar differs from the
electricCar though. It's motor is different, it uses gas and isn't electric.
Let's add two new method's to the
fuelTank. When you are initialing the
Car class for
motorCar what do you put for
kilowatts? What do you put for
fuelTank on the
You could put a null value and then do a conditional check to see if you should render it in the
information method or not. That will start to complicate the code more with a lot of conditional logic. How about we put the word "no" there for the time being, as we don't mind stating that these items aren't in our car.
Are there any issues?
This violates the principle as we are now depending on methods we don't use and have to cater for them.
A better way to approach this would be to carve out the similar methods into it's own class. We could create a
ElectricEngine class and pass the engine in when we initialise it. That way, the two classes are not tied together. For the
electricCar we can pass in an
ElectricEngine and for
motoCar we can pass in
GasEngine. Having the classes split up means we can compose the parts we want, to form our
Below is our refactored code. We've created a
ElectriEngine class with the relevant methods. We have also added an
information method to each engine, so that in
Car we can ask for the engines information and not care what engine it's using.
Why it is important?
- Composition. If we have many small interfaces, we can compose classes together with the parts we need.
- Decoupling. Tying your classes together with only relevant methods, will mean for a better developer experience. You will be able to refactor and change code more freely.
- Testing. Smaller classes are easier to test. It's a simpler object to create and test against.
Apply the solid principles you've learned so far and avoid large monolith classes/modules that do to much. When you have a large piece of code, look at how you could carve it up into smaller pieces. Compose classes together to form larger, more complex classes. To find out more about this look into composition over inheritance.
That's the interface segregation principle. If you have any questions about it or some of the ES6 syntax I've used, ask a question in the comments section.
If you want to see how another person explains it then check out the links below. They are both short videos which go over the same principle.