Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use inheritance only for polymorphism in c++

I´m designing a project in C++ and got to the point where I´m in doubt whereas I should use inheritance just to have polymorphism.

Specifically, I have a class JazzQuartet which has 4 objects: Saxophonist, Pianist, Bassist and Drummer, each with a play() and listen() methods, with different implementations.

I´d like to make them all inherit from a class Musician so I can have an array of Musician objects and call each one´s play() and listen() methods, and also so I can make any Musician listen() to any other. But as their implementation is completely different from each other, I´d be using this heritage just to get polymorphism, and I´m not sure if that´s a good design choice.

Any advice on this matter?

Thank you all in advance!

like image 376
Tomaz Fernandes Avatar asked Nov 29 '22 10:11

Tomaz Fernandes


2 Answers

"... so I can have an array of Musician objects and call each one´s play() and listen() methods, and also so I can make any Musician listen() to any other."

Musician should be an abstract class, i.e. an interface:

 class Musician {
 public:
     virtual void play() = 0;
     virtual void listen(Musician& other) = 0;
     virtual bool isPlaying() = 0;
     virtual ~Musician() {}
 };

And yes it's considered good design to promote interfaces.

This way you enforce that derived classes must implement these functions, and allow clients to access Musician instances, without need to know the concrete derived type.

As you've been asking to store the whole ensemble into an array:

With the above design you can use an array of std::unique_ptr<Musician> to aggregate a particular ensemble of musicians.

std::vector<std::unique_ptr<Musician>> jazzQuartet(4);
std::unique_ptr<Saxophonist> sax = new Saxophonist();
std::unique_ptr<Pianist> piano = new Pianist();
std::unique_ptr<Bassist> bass = new Bassist();
std::unique_ptr<Drummer> drums = new Drummer();

jazzQuartet[0] = sax;
jazzQuartet[1] = piano;
jazzQuartet[2] = bass;
jazzQuartet[3] = drums;

// And wire them up as necessary
//------------------------------------

// Usually everyone in the combo needs to listen to the drums
sax->listen(*drums);
piano->listen(*drums);
bass->listen(*drums);

...

// Let them all play
for(auto& m : jazzQuartet) { // Note the & to avoid copies made for the items
    m->play();
}
like image 110
πάντα ῥεῖ Avatar answered Dec 04 '22 12:12

πάντα ῥεῖ


I see no reason to worry that your Musician implementations will not have any common code. In fact this is exactly what is called Pure Abstract Class. In general, this is good idea to separate interface concepts.

It gives you more advantages then the ones you have mentioned, most important that you will most probably find that your other code does not need to know what particular type of Musician it is working with, and thus your main code will be simpler.

And that's also not "just" polymorphism, it also promotes encapsulation a lot as the users will be forced to use Musician interface.

Also, I think that in future you might find that you actually need some common code between different musicians (say, the director/conductor object reference?).

like image 42
Petr Avatar answered Dec 04 '22 12:12

Petr