После переосмысления на ночь и обращения к решению, которое Шон предоставил на В поисках лучшего способа, чем виртуальное наследование в C ++ , я предложил следующее решение.
Здесь я переопределяюпроблема быть более абстрактной, чтобы избежать путаницы в формах.У нас есть интерфейс Ball
, который может катиться, интерфейс FooBall
, который содержит методы, специфичные для Foo, и интерфейс FooBarBall
, который также является FooBall
и содержит методы, специфичные для Foo и Bar.Так же, как и в оригинальной задаче, у нас есть реализация FooBall
, и мы хотим вывести ее, чтобы охватить также специфические методы Bar.но наследование как интерфейса, так и реализации приведет к алмазному наследованию.
Чтобы решить эту проблему, вместо непосредственного помещения специфических методов Foo и Bar в производные интерфейсы Ball
, я поместил один метод в производное FooBall
интерфейс, который преобразует объект в Foo
объект с помощью метода toFoo()
.Таким образом, реализации могут смешиваться в независимом интерфейсе Foo
и Bar
, не вводя алмазное наследование.
Тем не менее, не все коды могут быть удалены, чтобы свободно выводить все столбцы из Foos.Нам все еще нужно написать независимые реализации Ball
, FooBall
и FooBarBall
, которые не наследуются друг от друга.Но мы можем использовать составной шаблон, чтобы обернуть реальные Foo
и Bar
объекты, которые реализованы по-разному.Таким образом, мы все еще можем исключить много кода, если у нас много реализаций Foo и Bar.
#include <stdio.h>
class Ball {
public:
// All balls can roll.
virtual void roll() = 0;
// Ball has many other methods that are not
// covered here.
virtual inline ~Ball() {
printf("deleting Ball\n");
};
};
class Foo {
public:
virtual void doFoo() = 0;
// do some very complicated stuff.
virtual void complexFoo() = 0;
virtual inline ~Foo() {};
};
/**
* We assume that classes that implement Bar also
* implement the Foo interface. The Bar interface
* specification failed to enforce this constraint
* by inheriting from Foo because it will introduce
* diamond inheritance into the implementation.
**/
class Bar {
public:
virtual void doBar() = 0;
virtual void complicatedBar() = 0;
virtual inline ~Bar() {};
};
class FooBall : public Ball {
public:
virtual Foo* toFoo() = 0;
virtual inline ~FooBall() {};
};
/**
* A BarBall is always also a FooBall and support
* both Foo and Bar methods.
**/
class FooBarBall : public FooBall {
public:
virtual Bar* toBar() = 0;
virtual inline ~FooBarBall() {};
};
/* Composite Implementation */
class FooImpl_A : public Foo {
public:
virtual void doFoo() {
printf("FooImpl_A::doFoo()\n");
};
virtual void complexFoo() {
printf("FooImpl_A::complexFoo()\n");
}
virtual inline ~FooImpl_A() {
printf("deleting FooImpl_A\n");
}
};
class FooBarImpl_A : public FooImpl_A, public Bar {
public:
virtual void doBar() {
printf("BarImpl_A::doBar()\n");
}
virtual void complicatedBar() {;
printf("BarImpl_A::complicatedBar()\n");
}
virtual inline ~FooBarImpl_A() {
printf("deleting FooBarImpl_A\n");
}
};
/* Composite Pattern */
class FooBarBallContainer : public FooBarBall {
public:
/* FooBarBallImpl_A can take any class that
* implements both the Foo and Bar interface,
* including classes that inherit FooBarImpl_A
* and other different implementations.
*
* We'll assume that realFoo and realBar are
* actually the same object as Foo methods have
* side effect on Bar methods. If they are not
* the same object, a third argument with false
* value need to be supplied.
*/
FooBarBallContainer( Foo* realFoo, Bar* realBar, bool sameObject=true ) :
_realFoo(realFoo), _realBar(realBar), _sameObject(sameObject) {}
virtual void roll() {
// roll makes use of FooBar methods
_realBar->doBar();
_realFoo->complexFoo();
}
virtual Foo* toFoo() {
return _realFoo;
}
virtual Bar* toBar() {
return _realBar;
}
virtual ~FooBarBallContainer() {
delete _realFoo;
// Check if realFoo and realBar are
// not the same object to avoid deleting
// it twice.
if(!_sameObject) {
delete _realBar;
}
}
private:
Foo* _realFoo;
Bar* _realBar;
bool _sameObject;
};
/* Monolithic Implmentation */
class FooBarBallImpl_B : public FooBarBall,
public Foo, public Bar {
public:
virtual void roll() {
complicatedBar();
doFoo();
}
virtual Foo* toFoo() {
return (Foo*) this;
}
virtual Bar* toBar() {
return (Bar*) this;
}
virtual void doFoo() {
printf("FooBarBallImpl_B::doFoo()\n");
}
virtual void complexFoo() {
printf("FooBarBallImpl_B::complexFoo()\n");
}
virtual void doBar() {
printf("FooBarBallImpl_B::doBar()\n");
}
virtual void complicatedBar() {
printf("FooBarBallImpl_B::complicatedBar()\n");
}
};
/* Example usage of FooBarBall */
void processFooBarBall(FooBarBall *ball) {
Foo *foo = ball->toFoo();
foo->doFoo();
ball->roll();
Bar *bar = ball->toBar();
bar->complicatedBar();
}
main() {
FooBarImpl_A *fooBar = new FooBarImpl_A();
FooBarBall *container = new FooBarBallContainer(fooBar, fooBar);
printf
processFooBarBall(container);
delete container;
FooBarBallImpl_B *ball = new FooBarBallImpl_B();
processFooBarBall(ball);
// we can even wrap FooBarBallImpl_B into the container
// but the behavior of roll() will become different
container = new FooBarBallContainer(ball, ball);
processFooBarBall(container);
delete container;
}