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.

The rule

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.

Example

Here we have a class called Car with a list of methods on it, such as turnOn, turnOff, 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. The 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 Car class, combustionEngine and fuelTank. When you are initialing the Car class for motorCar what do you put for rechargableBattery, electricMotor and kilowatts? What do you put for combustionEngine and fuelTank on the electricCar?

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 GasEngine and 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 Car.

Refactor

Below is our refactored code. We've created a GasEngine and 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.

Wrap up

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.


650 0 5