views:

163

answers:

3

Hello,

I have built several classes (A, B, C...) which perform operations on the same BaseClass. Example:

struct BaseClass {
    int method1();
    int method2();
    int method3();
}
struct A { int methodA(BaseClass& bc) { return bc.method1(); } }
struct B { int methodB(BaseClass& bc) { return bc.method2()+bc.method1(); } }
struct C { int methodC(BaseClass& bc) { return bc.method3()+bc.method2(); } }

But as you can see, each class A, B, C... only uses a subset of the available methods of the BaseClass and I'd like to split the BaseClass into several chunks such that it is clear what it used and what is not. For example a solution could be to use multiple inheritance:

// A uses only method1()
struct InterfaceA { virtual int method1() = 0; }
struct A { int methodA(InterfaceA&); }

// B uses method1() and method2()
struct InterfaceB { virtual int method1() = 0; virtual int method2() = 0; }
struct B { int methodB(InterfaceB&); }

// C uses method2() and method3()
struct InterfaceC { virtual int method2() = 0; virtual int method3() = 0; }
struct C { int methodC(InterfaceC&); }

The problem is that each time I add a new type of operation, I need to change the implementation of BaseClass. For example:

// D uses method1() and method3()
struct InterfaceD { virtual int method1() = 0; virtual int method3() = 0; }
struct D { int methodD(InterfaceD&); }

struct BaseClass : public InterfaceA, public InterfaceB, public InterfaceC
// here I need to modify the existing code to add class D
{ ... }

Do you know a clean way I can do this?

Thanks for your help

edit:

I forgot to mention that it can also be done with templates. But I don't like this solution either because the required interface does not appear explicitly in the code. You have to try to compile the code to verify that all required methods are implemented correctly. Plus, it would require to instantiate different versions of the classes (one for each BaseClass type template parameter) and this is not always possible nor desired.

A: 

I see nothing wrong with a derived class that does not use some elements of its base class.

A derived class is not, and should not be, required to use everything available to it.

Hence, I see nothing wrong with your original implementation, and no need to refactor it.

abelenky
Hi, thanks for taking time to answer.Fact is: class `A` only needs a class which implements `InterfaceA`. Making `A` depend on /all/ of `BaseClass`'s methods makes it difficult to re-use. For example, it would be too painful to require `BaseClass2` to implement all of `BaseClass`'s methods just because I want to be able to use it with `A` (and not `B` or `C`).Do you see the point?
caas
A: 

Should the callers of methodA, methodB, ... know which of the methods 1,2,3 are actually used?

If they do, you could split up the BaseClass in 3 different interfaces, and add exactly the interface arguments needed by the methods 1, 2 and 3, like this:

class Interface1 {virtual int method1() = 0;};
class Interface2 {virtual int method2() = 0;};
class Interface3 {virtual int method3() = 0;};

class A
   {
   int methodA(Interface1 &i1)
      {
      return i1.method1();
      }
   };

class B
   {
   int methodB(Interface1 &i1, Interface2 &i2)
      {
      return i1.method1() + i2.method2();
      }
   };

class C
   {
   int methodC(Interface2 &i2, Interface3 &i3)
      {
      return i2.method2() + i3.method3();
      }
   };

The caller can still choose to inherit from multiple interfaces, like this:

class MyClass : public Interface2, public Interface3
   {
   int method2() {...}
   int method3() {...}
   };
...
MyClass myClass;
C c;
c.methodC(myClass,myClass);
Patrick
Don't forget that in C++ `method1()`, `method2()`, and `method3()` have to be virtual, or polymorphism won't kick in.
In silico
Depending on what `BaseClass::methodN` will do, it isn't necessary, to make the methods virtual. Furthermore there is always a possibility for Non-virtual Interfaces. (http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface)
ablaeul
@In silico, you're right. I was a bit hasty. Edited it to make it correct again.
Patrick
caas
A: 

use pattern Adapter

struct InterfaceA { virtual int method1() = 0; }

struct A { int methodA(InterfaceA& bc) { return bc.method1(); } }

struct BaseClassAdapterForA: public InterfaceA 
{ 
   BaseClassAdapterForA(BaseClass& _bc) : bc(_bc) {}
   virtual int method1() 
   { 
       //note that the name of BaseClass method 
       //can be anything else
        return bc.method1(); 
        // or return bc.whatever();
   }
private:
   BaseClass& bc;
};
//usage
BaseClass bc;
A a;
BaseClassAdapterForA bca(bc);
a.methodA(bca);
Alsk
@Alsk That's exactly what I need! Thanks
caas