Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ class design having multiple interfaces for each distinct behavior

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.

like image 535
John PMK Avatar asked Aug 06 '11 08:08

John PMK


People also ask

What happens if a class implement 2 interfaces having same method?

A class implementation of a method takes precedence over a default method. So, if the class already has the same method as an Interface, then the default method from the implemented Interface does not take effect. However, if two interfaces implement the same default method, then there is a conflict.

Can a class implement multiple interfaces?

Your class can implement more than one interface, so the implements keyword is followed by a comma-separated list of the interfaces implemented by the class. By convention, the implements clause follows the extends clause, if there is one.

Why you can implement multiple interfaces but can extend only one class?

An interface cannot contain instance fields. The only fields that can appear in an interface must be declared both static and final. An interface is not extended by a class; it is implemented by a class. An interface can extend multiple interfaces.

Can a class implement multiple interfaces C#?

C# allows that a single class can implement multiple interfaces at a time, and also define methods and variables in that interface.


1 Answers

I think the interviewer was expecting you to talk about Visitor Pattern.

Yes, visitor pattern lets you add new behavior(s) to existing structure of classes, without any further addition/derivation of class/interface to the structure. All that it requires you to implement the behavior only and the visitor pattern lets you add this behaviour to the structure of the classes.

Read this wiki entry; it explains the pattern:

  • Visitor pattern

Here is one simple implementation of visitor pattern:

class IDudeBehavior;

class IDude
{
   public:
      virtual ~IDude() {}
      virtual void accept(IDudeBehavior *behaviour) = 0 ;
};

class IDudeBehavior
{
   public:
      virtual ~IDudeBehavior() {}
      virtual void visit(IDude * ) = 0;
};

class Dude : public IDude
{
    public:
      virtual void accept(IDudeBehavior *behaviour)
      {
          behaviour->visit(this);
      }
};

class LaughDudeBehavior : public IDudeBehavior
{
  public:
     virtual void visit(IDude *dude) { cout << "Dude is Laughing" << endl; }
};

class WalkDudeBehavior : public IDudeBehavior
{
  public:
     virtual void visit(IDude *dude) { cout << "Dude is Walking" << endl; }
};
int main() {
        IDude *dude = new Dude();
        dude->accept(new LaughDudeBehavior());
        dude->accept(new WalkDudeBehavior());
        return 0;
}

Online demo : http://ideone.com/Kqqdt

As of now, the class Dude has only two behaviors, namely LaughDudeBehavior and WalkDudeBehavior but since its a visitor pattern you can add any number of behavior to the Dude, without editing the class Dude. For example, if you want to add EatDudeBehavior and StudyCplusCplusDudeBehavior, then all you need to implement IDudeBehavior as:

class EatDudeBehavior : public IDudeBehavior
{
  public:
     virtual void visit(IDude *dude) { cout << "Dude is Eating" << endl; }
};

class StudyCplusCplusDudeBehavior : public IDudeBehavior
{
  public:
     virtual void visit(IDude *dude) { cout << "Dude is Studying C++" << endl; }
};

And then you need to accept these behavior as:

dude->accept(new EatDudeBehavior ()); 
dude->accept(new StudyCplusCplusDudeBehavior ());

Demo after adding these new behaviors : http://ideone.com/9jdEv


Avoid Memory Leak

There is one problem with the above code. Everything looks fine, except that it leaks memory. The program creates many instances of classes using new, but it never deallocated them using delete. So you need to think about this as well.

Memory leak can be fixed very easily as:

int main() {
        IDude *dude = new Dude();

        std::vector<IDudeBehavior*>  behaviours;
        behaviours.push_back(new LaughDudeBehavior());
        behaviours.push_back(new WalkDudeBehavior());
        behaviours.push_back(new EatDudeBehavior());
        behaviours.push_back(new StudyCplusCplusDudeBehavior());

        for(size_t i = 0 ; i < behaviours.size() ; i++ )
           dude->accept(behaviours[i]);

        //deallcation of memory!
        for(size_t i = 0 ; i < behaviours.size() ; i++ )
           delete behaviours[i];
        delete dude;
        return 0;
}

No memory leak now. :-)

like image 76
Nawaz Avatar answered Nov 15 '22 09:11

Nawaz