Hi DMX,
The way you're asking these questions makes me think that you may find the "interface segregation principle" useful. The basis of this principle says that you should try to split your interfaces into single responsibility classes that can then be
combined for an ability to provide specific and discrete functionality for inheritors.
Consider a contrived example, where you're creating a class to represent a multi-function printer.
A beginner's approach may be the following:
1 2 3 4 5 6
|
class MultifunctionPrinter : public IMachine
{
void print(Document* document) override { //implementation }
void scan(Document* document) override { //implementation }
void fax(Document* document) override { //implementation }
};
| |
where
IMachine
is an abstract interface that declares the base functionality.
1 2 3 4 5 6
|
class IMachine
{
void print(Document* document) = 0;
void scan(Document* document) = 0;
void fax(Document* document) = 0;
};
| |
That looks well and good for a multi-purpose printer, and if we needed to add functionality to the printer, we'd just stick another
function
in the
IMachine
class and override it in the multi-function printer. Sounds reasonable right?
Well, the problem becomes (and I think your question kind of alluded to this), that you then have to continue to add a bunch of functionality to a polluted base class. What if we wanted to inherit from
IMachine
but only implement a printer with scanning and printing? Or just faxing and printing? Or just faxing and scanning? Or just scanning? Or just printing? When we inherit from
IMachine
we get a ton of extra functionality that we don't need!
A better approach is to create multiple abstract interfaces and combine them for a more flexible combination:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
class IPrinter
{
void print(Document* doc) = 0;
};
class IScanner
{
void scan(Document* doc) = 0;
};
class IFaxer
{
void fax(Document* doc) = 0;
};
| |
The to make a
MultiPurposePrinter
we can combine these via multiple inheritance:
1 2 3 4
|
class MultiPurposePrinter : public IPrinter, IScanner, IFaxer
{
// Overrride print, scan and fax.
};
| |
But if we just wanted a printer that could print and fax:
1 2 3 4
|
class PrinterWithFax : public IPrinter, IFaxer
{
// Overrride print and fax.
};
| |
Or a scanner with the ability to fax:
1 2 3 4
|
class ScannerWithFax : public IScanner, IFaxer
{
// Overrride scan and fax.
};
| |
You can see that this code is much more flexible and keeps the separation of your base classes much cleaner.
HTH,
Aaron