I have a C++ module that needs to get information from other classes, without knowing those classes. The obvious approach is to use interfaces.
Let me give you an example. Suppose I have a library that manages books, and all books have their own characteristics and functionalities, and to allow the library to get a characteristic from a book or execute a functionaity, the book needs to implement an interface. Like this:
class Library
{
public:
void addBook(IBook &book);
};
class IBook
{
public:
string getAuthor() = 0;
string getTitle() = 0;
string getISBNCode() = 0;
size_t getNofPages() = 0;
size_t getNofImages() = 0;
double getPrice() = 0;
void printBook() = 0;
void convertToPdf() = 0;
};
Unfortunately, it does not make sense to implement all these methods for all kinds of books.
- Some books don’t have images (so I don’t want to implement getNofImages())
- Some books don’t have an ISBN code
- Some books can’t be bought, so they don’t have a price
- Some books can’t be printed
- Some books can’t be converted to PDF
Because I only have 1 interface, I am forced to implement everything for all books and return 0, return “” or do nothing int he implementation if it is irrelevant.
An alternative could be to split these interfaces in many interfaces, like this:
class IBook
{
public:
string getAuthor() = 0;
string getTitle() = 0;
size_t getNofPages() = 0;
};
class IISBNGetter
{
public:
string getISBNCode() = 0;
};
class IImagesGetter
{
public:
size_t getNofImages() = 0;
};
class IBuyable
{
public:
double getPrice() = 0;
};
class IPrintable
{
public:
void printBook() = 0;
};
class IConvertible
{
public:
void convertToPdf() = 0;
};
Book classes then only need to implement the interfaces they really want to support.
Adding a book to the library then becomes something like this:
bookid = myLibrary->addBook (myBook);
myLibrary->setISBNGetter (bookid, myBook);
myLibrary->setImageGetter (bookid, myBook);
myLibrary->setBuyable (bookid, myBook);
The advantage of having different interfaces is that it is clear for the library who supports what, and it never has the risk of calling something that is simply not supported.
However, since every book can have any possible combination of characteristics/functionalities, I end up with lots of interfaces with only 1 method.
Isn’t there a better way to organize the interfaces to obtain something like this?
I was also thinking about using Lambda expressions but behind the screens this is almost the same as having many many interfaces with only 1 method.
Any ideas?
I’d have IBook to implement every method:
since your alternative is too messy for me, you’d end with lots of confusing interfaces. Otherways, you could check if the Visitor pattern could be applied here.