Interface Segregation Principle in C++ is the fourth & by far the simplest design principle of a series SOLID as a Rock design principles. The SOLID design principles focus on developing software that is easy to maintainable, reusable & extendable. In this article, we will see a code violating ISP, a solution to the same code, guideline & benefits of ISP.
By the way, If you haven’t gone through my previous articles on design principles, then below is the quick links:
The code snippets you see throughout this series of articles are simplified not sophisticated. So you often see me not using keywords like override, final, public(while inheritance) just to make code compact & consumable (most of the time) in single standard screen size. I also prefer struct instead of class just to save line by not writing “public:” sometimes and also miss virtual destructor, constructor, copy constructor, prefix std::, deleting dynamic memory, intentionally. I also consider myself a pragmatic person who wants to convey an idea in the simplest way possible rather than the standard way or using Jargons.
Note:
Clients should not be forced to depend on interfaces that they do not use.
struct Document;
struct IMachine {
virtual void print(Document &doc) = 0;
virtual void fax(Document &doc) = 0;
virtual void scan(Document &doc) = 0;
};
struct MultiFunctionPrinter : IMachine { // OK
void print(Document &doc) override { }
void fax(Document &doc) override { }
void scan(Document &doc) override { }
};
struct Scanner : IMachine { // Not OK
void print(Document &doc) override { /* Blank */ }
void fax(Document &doc) override { /* Blank */ }
void scan(Document &doc) override {
// Do scanning ...
}
};
/* -------------------------------- Interfaces ----------------------------- */
struct IPrinter {
virtual void print(Document &doc) = 0;
};
struct IScanner {
virtual void scan(Document &doc) = 0;
};
/* ------------------------------------------------------------------------ */
struct Printer : IPrinter {
void print(Document &doc) override;
};
struct Scanner : IScanner {
void scan(Document &doc) override;
};
struct IMachine : IPrinter, IScanner { };
struct Machine : IMachine {
IPrinter& m_printer;
IScanner& m_scanner;
Machine(IPrinter &p, IScanner &s) : printer{p}, scanner{s} { }
void print(Document &doc) override { printer.print(doc); }
void scan(Document &doc) override { scanner.scan(doc); }
};
=> Faster Compilation
=> Maintainability
MyMachine : IMachine
to
MyMachine : IPrinter, IScanner, IFaxer
Do I need all the methods on this interface I’m using?
Even though big interfaces are a potential problem, the ISP isn’t about the size of interfaces. Rather, it’s about whether classes use the methods of the interfaces on which they depend. So ISP is poor guidance when designing software, but an excellent indicator of whether it’s healthy or not.
Previously published at http://www.vishalchovatiya.com/interface-segregation-principle-in-cpp-solid-as-a-rock/