Interface Segregation Principle with C++ Abstract Classes
The Interface Segregation Principle (ISP)—the "I" in the SOLID design
principles—dictates that clients should not be forced to rely on
interfaces they do not use. In C++, which lacks a dedicated
interface keyword, this principle is realized through pure
abstract classes. By breaking down large, monolithic class definitions
into smaller, role-specific abstract interfaces featuring pure virtual
functions, C++ developers can create decoupled, maintainable, and highly
reusable codebases. This article examines how C++ implements ISP using
pure abstract classes, the architectural issues caused by "fat"
interfaces, and how multiple inheritance facilitates targeted interface
implementation.
The Role of Pure Abstract Classes as C++ Interfaces
A pure abstract class in C++ serves as an interface contract. It is
defined as a class that contains at least one pure virtual function
(designated by = 0) and typically contains no member
variables or implementation logic, except for a virtual destructor.
class IPrintable {
public:
virtual ~IPrintable() = default;
virtual void print() const = 0;
};Because C++ does not provide a separate interface
construct like Java or C#, pure abstract classes represent the primary
language mechanism for establishing compile-time and runtime polymorphic
boundaries.
The Problem: "Fat" Interfaces
Violating the Interface Segregation Principle results in "fat" or "polluted" interfaces. A fat interface attempts to do too much by grouping unrelated actions into a single abstract class.
Consider a multi-function printer design:
// Violation of ISP
class IMachine {
public:
virtual ~IMachine() = default;
virtual void print() = 0;
virtual void scan() = 0;
virtual void fax() = 0;
};If a client only requires a simple, dedicated printer, implementing
IMachine forces that class to provide dummy implementations
or throw runtime exceptions for scan() and
fax(). This introduces unnecessary dependencies, increases
the risk of runtime errors, and forces recompilation of dependent
modules whenever any part of IMachine changes.
Applying ISP with Granular Abstract Classes
To conform to ISP, the broad contract is decomposed into lean, cohesive abstract classes based on specific client requirements.
class IPrinter {
public:
virtual ~IPrinter() = default;
virtual void print() = 0;
};
class IScanner {
public:
virtual ~IScanner() = default;
virtual void scan() = 0;
};
class IFax {
public:
virtual ~IFax() = default;
virtual void fax() = 0;
};By segregating the interfaces:
- A basic printer class implements only
IPrinter. - A dedicated scanning station implements only
IScanner. - An all-in-one office machine can implement
IPrinter,IScanner, andIFaxsimultaneously.
Implementing Multiple Interfaces via Multiple Inheritance
Because C++ natively supports multiple inheritance, a concrete class can inherit from multiple pure abstract classes without the data redundancy issues traditionally associated with multiple inheritance (often referred to as the "diamond problem"). Since pure abstract classes declare behavior without internal state, inheriting from several of them is structurally safe and idiomatic.
class MultiFunctionDevice : public IPrinter, public IScanner, public IFax {
public:
void print() override {
// Implementation for printing
}
void scan() override {
// Implementation for scanning
}
void fax() override {
// Implementation for faxing
}
};Architectural Benefits in C++
Applying ISP via pure abstract classes provides distinct advantages within C++ projects:
- Decoupled Client Code: Functions only accept
pointers or references to the specific interface they require (e.g.,
void executePrintJob(IPrinter& printer)), preventing them from accessing or depending on scanning or faxing capabilities. - Minimized Compilation Cascades: Modifications to
IScannerdo not trigger recompilation of translation units that rely strictly onIPrinter. - Simplified Testing and Mocking: Smaller interfaces require fewer mocked methods in unit tests, reducing boilerplate and test maintenance overhead.