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

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

本文关键字:何重用 直观 代码 关系 C++ 对象      更新时间:2023-10-16

如果这有点含糊,但是我不知道如何在以下情况下重复使用代码。我正在使用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...

,但步行功能依赖于所谓的任何东西的位置。将生命形式的位置传递给了自己的会员功能,这感觉很奇怪,总体上感觉就像是一个非常笨拙的解决方案。

我不确定如何处理这种情况。有没有办法这样做直觉和优雅?我是否缺少相当明显的东西?

您可以实现这样的混合系统:

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

我不是要说组成比继承更优选。

我只是想回答奥里格纳尔问题的这一特定部分:

但是步行功能依赖于任何正在调用的位置 它。将生命形式的位置传给自己的成员感到很奇怪 功能,总体感觉就像是一个非常笨拙的解决方案。

我正在添加此答案,主要是为解决方案的完整解释,以及其他任何带有类似问题的人。

我使用了塔尔坦拉玛(Tartanllama)的类似混合蛋白的系统的方法。每种能力都可以获得自己的阶级,生命形式继承了所需的所有能力。

但是,仍然存在一个问题,即能够访问生命形式中包含的某些值的能力。

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

在Mixin中创建一个参考变量,并将其等于构造函数中的生命形式位置,如用户3528438所建议的,不起作用,创造了令人困惑的行为。

事实证明,解决此问题的解决方案很简单:纯虚拟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;
    }
};

纯虚拟功能迫使您在生命形式的类别中定义它们,允许混合蛋白访问其所需的东西,并可以在许多不同的生命形式上重复使用。

我认为该解决方案足够直观且可重复使用,因此我将继续前进。

我意识到这个问题有点含糊,为此表示歉意,但仍然谢谢大家的帮助。