Interface Segregation Principle | SOLID as a Rock
Clients should not be forced to depend on interfaces that they do not use.
Join the DZone community and get the full member experience.
Join For FreeInterface 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:
- SRP -- Single Responsibility Principle
- OCP -- Open/Closed Principle
- LSP -- Liskov Substitution Principle
- ISP -- Interface Segregation Principle
- DIP -- Dependency Inversion Principle
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:
- If you stumbled here directly, then I would suggest you go through What is design pattern? first, even if it is trivial. I believe it will encourage you to explore more on this topic.
- All of this code you encounter in this series of articles are compiled using C++20(though I have used Modern C++ features up to C++17 in most cases). So if you don't have access to the latest compiler you can use https://wandbox.org/ which has preinstalled boost library as well.
Intent
Clients should not be forced to depend on interfaces that they do not use.
- Interface Segregation Principle is very much related to the Single Responsibility Principle. What it really means is that you should always design your abstractions in such a way that the clients that are using the exposed methods do not have to get the whole pie instead. That imposing the clients with the burden of implementing methods that they don’t actually need.
Violating the Interface Segregation Principle
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 ...
}
};
- As you can see, as far as
MultiFunctionPrinter
was concerned it's ok to implementprint()
,fax()
&scan()
methods enforced byIMachine
interface. - But what if you only need a
Scanner
orPrinter
, some dev still inheritsIMachine
& leave unnecessary methods blank or throwNotImplemented
exception, either way, you are doing it wrong.
Interface Segregation Principle Example
xxxxxxxxxx
/* -------------------------------- 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); }
};
- This gives the flexibility for the clients to combine the abstractions as they may see fit and to provide implementations without unnecessary cargo.
- As explained in the Single Responsibility Principle. You should avoid classes & interfaces with multiple responsibilities. Because they change often and make your software hard to maintain. You should try to split up the interface into multiple interfaces based on role.
Benefits
=> Faster Compilation
- If you have violated ISP i.e. stuffed methods together in the interface, and when method signature changes, you need to recompile all the derived classes. This is an important aspect for some compiled languages like C++ which is well known for slow compilation. While another way around is self explainable.
=> Reusability
- Martin also mentions that "fat interfaces" — interfaces with additional useless methods — lead to inadvertent coupling between classes. Thus, an experienced dev knows coupling is the bane of reusability.
=> Maintainability
- The much more universal ISP benefit is that by avoiding unneeded dependencies, the system becomes
- easier to understand;
- lighter to test;
- quicker to change.
- Similarly, to the reader of your code, it would be harder to get an idea of what your class does from the class declaration line. So, if dev sees only the one god-interface that may have inherited other interfaces it will likely not be obvious. Compare
xxxxxxxxxx
MyMachine : IMachine
to
xxxxxxxxxx
MyMachine : IPrinter, IScanner, IFaxer
- The latter tells you a lot, the former makes you guess at best.
Yardstick to Craft Interface Segregation Principle Friendly Software
- This principle comes naturally when you start decomposing your problem space by identifying major roles that take part in your domain. Hence, it's never a mechanical action.
- Following a single question to your self may help you to rectify your design:
Do I need all the methods on this interface I'm using?
Closing Notes
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.
Have Any Suggestions, Query or Wants to Say Hi
? Take the Pressure Off, You Are Just a Click Away.
Published at DZone with permission of Vishal Chovatiya. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments