在 C++ 中,当对象关系不直观时如何重用代码?

In C++, how to reuse the code when object relationship isn't intuitive?

抱歉,如果这有点含糊,但我不知道在以下情况下如何进行代码重用。我正在使用 C++。

该程序是一个简单的模拟程序,其中包含一些不同的内容。

struct StupidBug;
struct SmartBug;
struct Plant;
struct Mammal;

这些东西中的每一个都有它们能够做的一组事情。

struct StupidBug
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can spawn,
};

struct SmartBug
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can see extended surroundings,
    // Can spawn,
};

struct Plant
{
    // Can spawn
    // Can see surroundings
};

struct Mammal
{
    // Can walk,
    // Can eat,
    // Can see surroundings,
    // Can see extended surroundings,
};

这些生命体之间的功能有很多重叠,但我不知道如何在它们之间共享代码。对于所有这些,我想不出任何合适的继承形式。我尝试了作曲,按照以下方式做了一些事情:

struct AbilityToWalk
{
    void walk(position, direction)
    {
        // walk code
    }
};

struct StupidBug
{
    AbilityToWalk walk;
    // other abilities
};

struct SmartBug
{
    AbilityToWalk walk;
    // other abilities
};

// etc...

但是 walk 函数依赖于调用它的任何位置。将生命体的位置传递给它自己的成员函数感觉很奇怪,总体感觉是一个非常笨拙的解决方案。

我不确定如何处理这种情况。有没有一种直观而优雅的方法来做到这一点?我是不是漏掉了一些很明显的东西?

你可以像这样实现一个 mixin 系统:

struct Walker
{
    virtual void walk();    
};
struct Spawner
{
    virtual void spawn();   
};
struct Seer
{
    //seeing extended surroundings could be a specialized version of this
    virtual void see(); 
};
struct Eater
{
    virtual void eat(); 
};

struct Plant : Spawner, Seer
{
    //inherit default see function
    void spawn() override; //override spawn function
};

通过这种方式,您可以获得代码重用和接口的精细粒度,让您可以执行以下操作:

std::vector<std::unique_ptr<Walkers>> walkers;
walkers.push_back(std::make_unique<Mammal>());
//...
for (auto&& walker : walkers) walker->walk();
    class AbilityToWalk
    {
    public:
        AbilityToWalk(Type& position)
            : mPosition(position){}
        void walk(Type direction)
        {
            walk(mPosition, direction);
        }
    private:
        void walk(Type& position, Type direction)
        {
            // walk code
        }
    private:
        Type& mPosition;
    };
struct StupidBug
{
    StupidBug():walk(mPosition){}
    AbilityToWalk walk;
    Type mPosition;
    // other abilities
};

我并不是说组合优于继承。

我只是想回答原始问题的这个特定部分:

But the walk function relies on the position of whatever is calling it. It felt odd passing the position of a lifeform to it's own member function, and overall felt like a very clumsy solution.

我添加这个答案主要是为了对解决方案进行完整的解释,以及其他任何发现它有类似问题的人。

我采用了 TartanLlama 的类混合系统方法。每个能力都有自己的class,生命形式继承它需要的所有能力。

但是,仍然存在一个问题,即能力需要能够访问生命体中包含的某些值,例如它的位置。

class Mover
{
public:

    virtual void move(Vec2 direction)
    {
                   /*the problem*/
        Vec2 newPos = position + direction;
        position = newPos;
    }
};


class DumbBug : public Mover
{
public:
    /*the problem*/
    Vec2 position;

    DumbBug(Vec2 pos)
        : position(pos)
    {}
};

按照 user3528438 的建议,在 mixin 中创建一个引用变量并将其设置为等于生命形式在构造函数中的位置是行不通的,这会产生令人困惑的行为。

事实证明,这个问题的解决方案非常简单:纯虚拟 getters/setters。

class Mover
{
public:
    virtual void move(Vec2 direction)
    {
                     /*the solution*/
        Vec2 newPos = get_position() + direction;
        set_position(newPos);
    }

    /*--- the solution -------------------*/
    virtual Vec2 get_position() = 0;
    virtual void set_position(Vec2 pos) = 0;
};


class DumbBug : public Mover
{
private:
    Vec2 position;

public:
    DumbBug(Vec2 pos)
        : position(pos)
    {}

    /*- the solution -------------------*/
    virtual Vec2 get_position() {
        return position;
    }
    virtual void set_position(Vec2 pos) {
        position = pos;
    }

};

纯虚函数迫使您在生命形式中定义它们 class,允许 mixin 访问它需要的东西,并可在许多不同的生命形式中重用。

我认为此解决方案足够直观且可重复使用,因此这是我要继续推进的解决方案。

我知道这个问题有点含糊,对此深表歉意,但是none谢谢大家的帮助。