问题
I'm implementing an architecture when i have a container of heterogeneous objects which may have or not some common methods-attribute. I need to cycle through them and apply some functions, update some members, and call some methods over the various interfaces.
I've come up to what i believe a "standard" architecture, based on inheritance:
#include <vector>
#include <memory>
#include <iostream>
using namespace std;
struct Base {
virtual ~Base() {}
};
struct PositionInterface {
int x = 0;
int y = 0;
virtual ~PositionInterface() {}
};
struct DrawInterface {
void draw() { cout << "Here i am" << endl; }
virtual ~DrawInterface() {}
};
struct ChargeInterface {
int charge = 100;
virtual ~ChargeInterface() {}
};
struct LifeInterface {
int life = 100;
virtual ~LifeInterface() {}
};
struct A: public Base,
public LifeInterface, public PositionInterface {};
struct B: public Base,
public DrawInterface, public PositionInterface, public ChargeInterface {};
int main() {
std::vector<std::shared_ptr<Base>> vec;
vec.push_back(make_shared<A>());
vec.push_back(make_shared<B>());
for (auto & el : vec) {
auto p = dynamic_cast<PositionInterface *>(el.get());
if (p) {
p->x += 10;
p->y -= 10;
}
}
// ..other stuff
for (auto & el : vec) {
auto l = dynamic_cast<LifeInterface *>(el.get());
if (l) {
l->life -= 100;
}
}
// ..other stuff
for (auto & el : vec) {
auto d = dynamic_cast<DrawInterface *>(el.get());
if (d) {
d->draw();
}
}
}
Anyway what I'm looking too looks like a component based system. To me seems like that these interfaces could be components added via composition, not inheritance. Something like this:
struct A: public Base {
LifeInterface l;
PositionInterface p;
};
But then how could i cycle through the vector of Base
objects dynamic_cast
ing to the right interface?
Do you see any drawbacks in this kind of architecture (besides RTTI and public variables :-) ) ?
回答1:
"But then how could i cycle through the vector of Base objects dynamic_casting to the right interface?"
I'd propose to have real abstract interfaces, like this instead:
struct Base {
virtual ~Base() {}
};
struct PositionInterface {
virtual int x() const = 0;
virtual void x(int value) = 0;
virtual int y() const = 0;
virtual void y(int value) = 0;
virtual ~PositionInterface() {}
};
struct DrawInterface {
virtual void draw() const = 0;
virtual ~DrawInterface() {}
};
struct ChargeInterface {
virtual int charge() const = 0;
virtual ~ChargeInterface() {}
};
struct LifeInterface {
virtual int life() const {};
virtual ~LifeInterface() {}
};
Base implementation classes that can be used as mixins
class PositionBase : public PositionInterface {
public:
virtual int x() const { return x_; }
virtual void x(int value) { x_ = value; }
virtual int y() const { return y_; }
virtual void y(int value) { y_ = value; }
virtual ~PositionBase() {}
protected:
PositionBase() {}
int x_;
int y_;
};
class ChargeBase : public ChargeInterface {
public:
virtual int charge() const { return charge_; }
virtual ~ChargeInterface() {}
protected:
ChargeBase(int charge) : charge_(charge) {}
const int charge_;
};
class LifeBase : public LifeInterface {
public:
virtual int life() const { return life_; }
virtual ~LifeBase() {}
protected:
LifeBase(int life) : life_(life) {}
const int life_;
};
And have your implementations like follows
struct A
: public virtual Base
, public LifeBase
, public PositionBase {
A() : Base(), LifeBase(100), PositionBase() {}
};
struct B
: public virtual Base
, public DrawInterface
, public PositionBase
, public ChargeBase {
B() : Base(), PositionBase(), ChargeBase(100)
virtual void draw() const {
// Must implement draw()
}
};
- Don't use public member variables, as you can't really control these from inherited classes. Use virtual getter/setter functions as shown above.
- To check if the right interface is supported, use
dynamic_cast<>
. To perform operations on these, provide simple templated functions, like e.g.:
template<class T> void draw(const T& item) {
DrawInterface* drawableItem = dyn<mic_cast<DrawInterface*>(&item);
if(drawableItem) {
drawableItem->draw();
}
// Item isn't drawable, ignore or throw exception
}
回答2:
The main drawback of RTTI is using it, so making it impossible is mostly a good thing.
Use possibly-null pointers, interface-pointer-returning functions, or a boost::optional
-like type.
Example:
class Base
{
public:
virtual LifeInterface* life() { return 0; }
virtual PositionInterface* position() { return 0; }
};
class A: public Base {
public:
LifeInterface* life() { return &l; }
private:
LifeInterface l;
};
// ...
for (auto & el : vec) {
auto l = el.life();
if (l) {
l->life -= 100;
}
}
来源:https://stackoverflow.com/questions/25084962/c-is-a-components-based-architeture-implemented-via-inheritance-considered-goo