Извините, если это немного расплывчато, но я не знаю, как сделать повторное использование кода в следующей ситуации. Я использую 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...
Но функция ходьбы зависит от положения того, что ее вызывает. Было странно передавать положение жизненной формы ее собственной функции-члену, и в целом казалось очень неуклюжим решением.
Я не уверен, как поступить с этой ситуацией. Есть ли способ сделать это интуитивно понятным и элегантным? Я что-то упускаю довольно очевидное?
Вы могли бы реализовать систему 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
};
Я не пытаюсь сказать, что композиция предпочтительнее наследования.
Я только пытаюсь ответить на эту конкретную часть первоначального вопроса:
Но функция ходьбы зависит от того, что вызывает
Это. Было странно передавать положение жизненной формы своему члену
функционировать, и в целом чувствовал себя очень неуклюжим решением.
Я добавляю этот ответ в основном для полного объяснения решения и для всех, кто найдет его с похожей проблемой.
Я пошел с методом 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;
}
};
Чистые виртуальные функции вынуждают вас определять их в классе жизненных форм, позволяя миксинам получать доступ к тому, что ему нужно, и быть многократно используемыми во многих различных жизненных формах.
Я думаю, что это решение достаточно интуитивно понятно и пригодно для повторного использования, поэтому я собираюсь двигаться дальше.
Я понимаю, что вопрос был немного неопределенным, извиняюсь за это, но тем не менее спасибо всем за помощь.