【问题标题】:C++ Is a components-based architeture implemented via inheritance considered good practice?C++ 通过继承实现的基于组件的架构是否被认为是好的做法?
【发布时间】:2014-09-24 22:28:34
【问题描述】:

当我有一个异构对象的容器时,我正在实现一个体系结构,这些对象可能有或没有一些常见的方法属性。我需要循环它们并应用一些功能,更新一些成员,并通过各种接口调用一些方法。

我已经提出了我认为基于继承的“标准”架构:

#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();
        }
    }
}

无论如何,我看起来也像一个基于组件的系统。在我看来,这些接口可以是通过组合而不是继承添加的组件。像这样的:

struct A: public Base {
    LifeInterface l;
    PositionInterface p;
};

但是我怎样才能通过Base对象dynamic_cast的向量循环到正确的界面?

您认为这种架构有什么缺点吗(除了 RTTI 和公共变量 :-))?

【问题讨论】:

  • 我不会提供任何公共变量,而只提供用于接口声明的纯虚拟 getter/setter/操作。

标签: c++ oop inheritance interface components


【解决方案1】:

RTTI 的主要缺点是使用它,所以让它变得不可能是一件好事。

使用可能为空的指针、接口指针返回函数或boost::optional-like 类型。

例子:

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;
    }
}

【讨论】:

    【解决方案2】:

    “但是我怎么能循环通过基础对象的向量 dynamic_casting 到正确的接口呢?”

    我建议使用 real 抽象接口,例如:

    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() {}
    };
    

    可用作 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_;
    };
    

    并让您的实现如下所示

    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()
        }
    };
    
    1. 不要使用公共成员变量,因为您无法从继承的类中真正控制这些变量。使用如上所示的虚拟 getter/setter 函数。
    2. 要检查是否支持正确的接口,请使用dynamic_cast&lt;&gt;。要对这些执行操作,请提供简单的模板化函数,例如:

    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
    }
    

    【讨论】:

      猜你喜欢
      • 2017-12-18
      • 2010-12-22
      • 1970-01-01
      • 2019-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-11
      相关资源
      最近更新 更多