This is my first post, so be kind. This is an interview question I recently got, but I couldn't find an answer after searching (google , C++FAQ etc).
There is an interface I1 with a behavior b1(). There are 3 classes A,B,C. All these classes are implementing the interface I1 by overriding b1(). There is a fourth class D which has behavior (b1) defined in interface I1 and an extra behavior b2
Question is how do you design the class D.
My answer was create another Interface I2 which defines behavior b2() and make class D implement both I1 and I2 (multiple Inheritance in C++) by overriding both b1() and b2()
Interviewer agreed to the solution, but asked what if in future new classes with new set of behaviors come up, how will we handle that
I could only think of adding more Interfaces (I3, I4 etc) and doing multiple inheritance, but I am aware that here you end up in a huge number of derived classes with corresponding Interfaces
The interviewer seemed to be expecting a better solution but he didn't reveal the answer. I would love to know how the experts here would solve this design problem.
PS: After serious thought on this I think the answer may lie in using a design pattern, but looking at common design patterns I could not find any that matched this problem
edit 1: I have more questions and clarifications, so editing the post here, not sure if this is right way or I need to post this as an answer to my own question.
First Let me thank @Nawaz, @Alexandre and @Sjoerd for your valuable inputs. I am just starting to learn the design aspects in C++/design patterns, so pardon my ignorance on the subject.
The example of Vistor pattern by @Nawaz was really helpful, but I guess it is only a particular case of the original problem asked by the interviewer. @Alexandre has correctly pointed out the scenarios here.
Let me explain another aspect of this. When we talk of behaviors ,we need to group them based on
1) common behaviors related to a group of objects or object. (This is intuitive or can be observed as in the real world) eg: behaviors of a Dude (taking the example of @Nawaz) - i,e walking, eating, studying etc
2) uncommon or very peculiar behaviors related to a group (This is counter intuitive) eg: just for argument sake, consider a Dude who composes music (I know this example is not perfect)
3) totally unrelated behavior to the group. I cannot think of an example, but my point is lets say for some wierd reason we need to give the object this behavior.
So I think Visitor pattern can solve the problem in 1) but I suspect it wont for 2) and 3).
taking the IDude example, we would need the following changes to make a Dude who can compose music.
class IComposerBehavior;
class IComposer
{
public:
virtual ~IComposer() {}
virtual void accept(IComposerBehavior *behaviour) = 0 ;
};
class IComposerBehavior
{
public:
virtual ~IComposerBehavior() {}
virtual void visit(IComposer * ) = 0;
};
class Dude : public IDude, public IComposer
{
public:
virtual void accept(IDudeBehavior *behaviour)
{
behaviour->visit(this);
}
virtual void accept(IComposerBehavior *behaviour)
{
behaviour->visit(this);
}
};
class SymphonyComposerBehavior : public IComposerBehavior
{
public:
virtual void visit(IComposer *dude) { cout << "Dude is composing a symphony" << endl; }
};
Similarly we need to change client code also to account for the SymphonyComposerBehavior.
So basically we ended up changing both the Dude class code and client code, which negates effect of the pattern.
I think the interviewer was asking about new behavior which cannot be put in a group of related behaviors which were previously identified. So in this case even if the classes are fixed can a visitor pattern solve as @Alexandre pointed out?
Let me give an example here just of the top of my head (not sure if this is a correct example to represent the problem). Lets say I need to design an application for a Robot manufacturing firm. The requirement gradually grows as
- Initially We are only producing Toy Robots
- Then Human helper Robots
- Then Self Healing Robots (would just correct itself when defective)
- Then Humanoid Robots
- Then machine Robots (that are not human like but as a substitute for any machine you can think of) . I have deliberately put this here even though its place should be before with a correct evolution scheme.
- finally Humanoid Robots with life (atleast we can dream :-) )
So if we know the complete list of Robots prior to designing the application, we could come with a better design, but how do we design when each new type is introduced sequentially in the above order. My point here is we know that a Robot has certain behaviors or characteristics, but when uncommon features have to be introduced later ( like self healing, machine robot) how do we go about?
Thanks.