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 method开发者_开发问答3()
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.
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);
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.
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);
精彩评论