The interface segregation principle is just about what its name suggests. It is formulated as follows:
That sounds pretty obvious, but it has some connotations that aren't that obvious. Firstly, you should prefer more but smaller interfaces to a single big one. Secondly, when you're adding a derived class or are extending the functionality of an existing one, you should think before you extend the interface the class implements.
Let's show this on an example that violates this principle, starting with the following interface:
class IFoodProcessor {
public:
virtual ~IFoodProcessor() = default;
virtual void blend() = 0;
};
We could have a simple class that implements it:
class Blender : public IFoodProcessor {
public:
void blend() override;
};
So far so good. Now say we want to model another, more advanced food processor and we recklessly tried to add more methods to our interface:
class IFoodProcessor {
public:
virtual ~IFoodProcessor() = default;
virtual void blend() = 0;
virtual void slice() = 0;
virtual void dice() = 0;
};
class AnotherFoodProcessor : public IFoodProcessor {
public:
void blend() override;
void slice() override;
void dice() override;
};
Now we have an issue with the Blender class as it doesn't support this new interface – there's no proper way to implement it. We could try to hack a workaround or throw std::logic_error, but a much better solution would be to just split the interface into two, each with a separate responsibility:
class IBlender {
public:
virtual ~IBlender() = default;
virtual void blend() = 0;
};
class ICutter {
public:
virtual ~ICutter() = default;
virtual void slice() = 0;
virtual void dice() = 0;
};
Now our AnotherFoodProcessor can just implement both interfaces, and we don't need to change the implementation of our existing food processor.
We have one last SOLID principle left, so let's learn about it now.