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!
"... 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();
}
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?).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With