Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Having one interface with lots of virtual methods? Or having many interfaces with only 1 virtual method?

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?

like image 209
Patrick Avatar asked Nov 19 '10 11:11

Patrick


3 Answers

I'd have IBook to implement every method:

class IBook
   {
   public:
      virtual ~IBook() {}

      virtual string getAuthor() { return ""; } // or some other meaningful default value
      virtual string getTitle() { return ""; }
      virtual string getISBNCode() { return ""; }
      virtual size_t getNofPages() { return 0; }
      virtual size_t getNofImages() { return 0; }
      virtual double getPrice() { return .0; }
      virtual void   printBook() {}
      virtual void   convertToPdf() {}
   };

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.

like image 141
Simone Avatar answered Nov 07 '22 06:11

Simone


A solution could be to keep your base interface with pure virtual methods, but have your actual implementations inherit from an intermediate class providing default implementations for the virtual methods.

One common implementation of that intermediate class would be to throw some kind of "MethodNotImplemented" exception in each method, so the user of the class can catch those on a case-by-case basis.

Seeing as exceptions are a bit expensive for such a case where calling non-existing methods would not be "exceptional", there is also the approach of having those methods being empty or returning default values, as Simone posted.

like image 24
SirDarius Avatar answered Nov 07 '22 06:11

SirDarius


I guess you don't need to get to any extremes but to choose middle way. Having one interface is not good it breaks Interface Segregation Principle (ISP) on other hand to having so many interfaces will spoil your code as well. I would leave one core IBook and think over the rest. For instance IPrintable and IConvertible(for pdf convert) may go in one interface. Probably IBuyable and IISBNGetter will alse go togehter.

like image 2
Arseny Avatar answered Nov 07 '22 07:11

Arseny