наследование — как в C ++, как повторно использовать код, когда объектные отношения не интуитивно понятны?

Извините, если это немного расплывчато, но я не знаю, как сделать повторное использование кода в следующей ситуации. Я использую 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...

Но функция ходьбы зависит от положения того, что ее вызывает. Было странно передавать положение жизненной формы ее собственной функции-члену, и в целом казалось очень неуклюжим решением.

Я не уверен, как поступить с этой ситуацией. Есть ли способ сделать это интуитивно понятным и элегантным? Я что-то упускаю довольно очевидное?

-1

Решение

Вы могли бы реализовать систему 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();
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
};

Я не пытаюсь сказать, что композиция предпочтительнее наследования.

Я только пытаюсь ответить на эту конкретную часть первоначального вопроса:

Но функция ходьбы зависит от того, что вызывает
Это. Было странно передавать положение жизненной формы своему члену
функционировать, и в целом чувствовал себя очень неуклюжим решением.

1

Я добавляю этот ответ в основном для полного объяснения решения и для всех, кто найдет его с похожей проблемой.

Я пошел с методом 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, не работает, создавая путанное поведение.

Оказывается, решение этой проблемы довольно простое: чисто виртуальные геттеры / сеттеры.

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

};

Чистые виртуальные функции вынуждают вас определять их в классе жизненных форм, позволяя миксинам получать доступ к тому, что ему нужно, и быть многократно используемыми во многих различных жизненных формах.

Я думаю, что это решение достаточно интуитивно понятно и пригодно для повторного использования, поэтому я собираюсь двигаться дальше.

Я понимаю, что вопрос был немного неопределенным, извиняюсь за это, но тем не менее спасибо всем за помощь.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector