-1

Apologies if this is sort of vague, but I don't know how to go about code reuse in the following situation. I'm using C++.

The program is a simple simulation and it has a few different things in play.

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

Each of these things have a set of things they are capable of.

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

There is quite a bit of overlap in functionality between these lifeforms but I don't know how to go about sharing the code between them. I can't think of any suitable form of inheritance for all of these. I tried composition, doing something along the lines of:

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

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

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

// etc...

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.

I'm not sure how to go about dealing with this situation. Is there a way to do this that is intuitive and elegant? Am I missing something fairly obvious?

Deepak Keynes
  • 311
  • 2
  • 16
djscrew
  • 57
  • 6

3 Answers3

1

You could implement a mixin system like this:

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

This way you get code reuse and fine granularity of interfaces, letting you do things like:

std::vector<std::unique_ptr<Walkers>> walkers;
walkers.push_back(std::make_unique<Mammal>());
//...
for (auto&& walker : walkers) walker->walk();
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • how could you add a Plant to a vector of Walkers? in your implementation a plant doesn't implement the Walker interface. – dau_sama May 04 '15 at 08:51
  • If any member other than `walk` need access to location, location needs to be protected, which really smells. – user3528438 May 04 '15 at 11:14
1
    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
};

I'm not trying to say that composition is preferred over inheritance.

I'm only trying to answer this particular part of the oirginal question:

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.

user3528438
  • 2,737
  • 2
  • 23
  • 42
  • I tried this and found that mPosition ends up being the same for every instance of the StupidBug. There were three of them on screen, in seperate positions, but the mPosition of each of their AbilityToWalk was the same. This resulted in only the last one walking around. – djscrew May 05 '15 at 17:52
  • @djscrew If `mPosition`s are the same, how can they be separate positions? – user3528438 May 05 '15 at 18:07
  • Thats exactly the problem. The position of the each bug would not line up with the mPosition of each Walker. It's hard to explain in words, [this paste](http://pastebin.com/zXj0PT5G) shows the current implementation and explains the problem more clearly. The paste uses the mixin implementation from the other answer, but the odd outcome was the same when I had set up the implementation in this answer. – djscrew May 05 '15 at 18:16
  • @djscrew your `DumbBug`'s `mPosition` overrides the `mPosition` in the base class `Walker` which makes it left uninitialized. You are lucky you didn't crash the program. – user3528438 May 05 '15 at 18:27
  • How would I go about fixing this? It won't compile if I remove mPosition from Walker and it segfaults if I don't declare mPosition as a reference. – djscrew May 05 '15 at 18:35
  • Never mind, I found a solution to my problems and will detail it in an answer when I have more time. – djscrew May 05 '15 at 19:48
0

I'm adding this answer mostly for a full explanation of the solution, and for anyone else that finds it with a similar problem.

I went with TartanLlama's method of a mixin-like system. Each ability gets it's own class, and the life-form inherits all of the abilities it needs.

However, there was still the problem that the abilities needed to be able to access certain values contained in the life-form, such as its position.

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

Creating a reference variable in the mixin and setting it equal to the life-forms position in a constructor, as suggested by user3528438, does not work, creating confusing behavior.

It turns out the solution to this problem is pretty simple: pure virtual 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;
    }

};

The pure virtual functions force you to define them in the life-form class, allowing for the mixin to access what it needs, and be reusable over many different life-forms.

I think this solution is sufficiently intuitive and reusable, so it's the one that i'm going to move forward with.

I realize the question was sort of vague, apologies for that, but none the less thank you all for helping out.

djscrew
  • 57
  • 6