Как определить «здоровье» и «maxHealth» в одном объекте, когда «здоровье» не может быть выше, чем «maxHealth»?

У меня есть класс под названием Warrior который имеет uint m_health а также uint m_maxHealth атрибутов.
Я хочу, чтобы мой конструктор принял параметры Warrior(uint health, uint maxHealth),

Сейчас я много изучал C ++ и знаю весь синтаксис и т. Д., Но трудно найти учебники о том, как использовать этот материал и т. Д., Поэтому я не знаю, как мне определить health а также maxHealth когда health очевидно, не может быть выше, чем maxHealth : /

Вот несколько методов, о которых я подумал:

// method 1
Warrior::Warrior(uint health, uint maxHealth) :
m_health((health > maxHealth) ? maxHealth : health),
m_maxHealth(maxHealth)
{}

// method 2
Warrior::Warrior(uint health, uint maxHealth) :
m_maxHealth(maxHealth)
{
if (health > maxHealth) {
m_health = maxHealth;
}
else {
m_health = health;
}
}

Я уверен, что есть и другие способы.
Извините, если это просто вопрос мнения, но если в C ++ есть «предпочтительный» способ, что бы это было?

2

Решение

Я бы просто поставил утверждение или выбросил исключение на случай, если предварительное условие не выполнено:

Warrior::Warrior(uint health, uint maxHealth) :
m_health(health),
m_maxHealth(maxHealth)
{
assert(health <= maxHealth);
// or throw exception
throw std::logic_error("explanation");
}

Я имею в виду, что является рациональным для звонящего, чтобы решить, какое значение взять? Если вызывающая сторона оценивает значение здоровья больше, чем maxHealth, — это нарушение логики вашего класса, поэтому я бы предложил немедленно выйти из строя и сообщить об этом вызывающей стороне. Это должно быть проблемой в программном обеспечении, поэтому я думаю, что это нежелательно скрывать.

Исключение или утверждение — это явный механизм, который поможет вам быстро обнаружить и распознать такие проблемы.

2

Другие решения

Если вы намеревались установить здоровье во время игры, вы можете рассмотреть что-то вроде этого:

Warrior::Warrior(unsigned int health, unsigned int maxHealth) :
m_maxHealth(maxHealth)
{
setHealth(health);
}

void Warrior::setHealth(unsigned int health)
{
m_health = std::min(health, m_maxHealth);
}
4

Существует «лучший» способ, в зависимости от вашего определения лучше. (И я предполагаю, что вы имеете в виду «короче» здесь)

Warrior::Warrior(unsigned int health, unsigned int maxHealth) :
m_health(std::min(health, maxHealth)),
m_maxHealth(maxHealth)
{
}
3

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

Другой подход будет использовать узкий контракт на ваш конструктор, в котором поведение объекта хорошо определено, только если очевидное ограничение, которое health < maxHealth правда. В этом случае вы должны задокументировать конструктор как таковой и реализовать его:

Warrior::Warrior(unsigned int health, unsigned int maxHealth)
: m_health(health), m_maxHealth(maxHealth)
{
assert(health <= maxHealth);
}

Что, на мой взгляд, является лучшим дизайном. Нет веских причин принимать неправильные аргументы в конструктор. Добавив, что assert там вы можете обнаружить в начале цикла разработки, когда ваш тип используется вне контракта. Это особенно важно для функций, которые имеют несколько аргументов одного и того же типа, поскольку это поможет обнаружить, если вы по ошибке передали аргументы не по порядку. Использование широкий контракт там уменьшит maxHealth быть таким же, как текущий healthи через некоторое время у вас возникнут проблемы с попыткой выяснить, почему Warrior слабее, чем вы думали узкий Контракт скажет вам, что аргументы вышли из строя немедленно.

3
template <typename T>
class RangeBoundValue
{
public:
RangeBoundValue(const T _min, const T _max) : _min(_min), _max(_max) {}

void setValue(T val)
{
if (val < _min) _value = _min;
else if (val > _max) _value = _max;
else _value = val;
}

T value() const {return _value;}

private:
const T _min;
const T _max;
T       _value;
};

Используйте этот вспомогательный класс внутри Warrior

class Warrior
{
...
private:
RangeBoundValue<uint> _health;
};
2

Я весьма удивлен, что люди предлагают генерировать исключения, когда список аргументов для конструктора имеет значение health> max_health. Например, скажем, у вас есть какая-то реализация воскрешения, где все существа воскресают с фиксированным количеством здоровья вплоть до максимального здоровья, а в некоторых случаях это превышает максимальное здоровье, чтобы избежать связывания, вы не хотите, чтобы этот метод сохранял все объекты. Максимальное здоровье, вы можете добавить методы для проверки перед отправкой этих аргументов конструкторам, но опять же это усложняет ситуацию. Максимальное здоровье обычно устанавливается как предел здоровья, и, учитывая, что это простое ограничение, не связанное ни с чем другим, воин должен нести ответственность за соблюдение своих собственных ограничений.

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