1 Небольшая вещь - зачем менять идентификатор организации?Обычно это константа и инициализируется во время построения, и вот так:
class CEntity
{
const int m_id;
public:
CEntity(int id) : m_id(id) {}
}
Для других вещей существуют разные подходы, выбор зависит от того, сколько есть функций, специфичных для типа (инасколько хорошо вы можете их реплицировать).
Добавить ко всем
Самый простой способ - просто добавить все методы в базовый интерфейс и реализовать их какno-op в классах, которые его не поддерживают.Это может звучать как плохой совет, но это приемлемая денормализация, если существует очень мало методов, которые не применяются, и вы можете предположить, что набор методов не будет значительно расти с будущими требованиями.
Вы можетедаже реализовать базовый вид «механизма обнаружения», например
class CEntity
{
public:
...
virtual bool CanMove() = 0;
virtual void Move(CPoint target) = 0;
}
Не переусердствовать! Легко начать этот путь, а затем придерживаться его, даже когда он создает огромный беспорядоквашего кода.Это может быть приукрашено как «преднамеренная денормализация иерархии типов» - но в конце концов это просто хак, который позволяет быстро решить несколько проблем, но быстро причиняет боль при росте приложения.
True Type discovery
с использованием и dynamic_cast
, вы можете безопасно привести ваш объект от CEntity
к CFastCat
.Если сущность на самом деле является CReallyUnmovableBoulder
, результатом будет нулевой указатель.Таким образом, вы можете проверять объект на предмет его действительного типа и реагировать на него соответствующим образом.
CFastCat * fastCat = dynamic_cast<CFastCat *>(entity) ;
if (fastCat != 0)
fastCat->Meow();
Этот механизм работает хорошо, если к методам, зависящим от типа, привязана только небольшая логика.Это не хорошее решение, если вы в конечном итоге получите цепочки, в которых вы будете проверять многие типы и действовать соответственно:
// -----BAD BAD BAD BAD Code -----
CFastCat * fastCat = dynamic_cast<CFastCat *>(entity) ;
if (fastCat != 0)
fastCat->Meow();
CBigDog * bigDog = dynamic_cast<CBigDog *>(entity) ;
if (bigDog != 0)
bigDog->Bark();
CPebble * pebble = dynamic_cast<CPebble *>(entity) ;
if (pebble != 0)
pebble->UhmWhatNoiseDoesAPebbleMake();
Это обычно означает, что ваши виртуальные методы не выбраны тщательно.
Интерфейсы
Выше можно расширить на интерфейсы, когда функциональность, зависящая от типа, - это не отдельные методы, а группы методов.Они не очень хорошо поддерживаются в C ++, но это терпимо.Например, ваши объекты имеют разные функции:
class IMovable
{
virtual void SetSpeed() = 0;
virtual void SetTarget(CPoint target) = 0;
virtual CPoint GetPosition() = 0;
virtual ~IMovable() {}
}
class IAttacker
{
virtual int GetStrength() = 0;
virtual void Attack(IAttackable * target) = 0;
virtual void SetAnger(int anger) = 0;
virtual ~IAttacker() {}
}
Ваши разные объекты наследуются от базового класса и одного или нескольких интерфейсов:
class CHero : public CEntity, public IMovable, public IAttacker
И снова вы можете использовать dynamic_cast для поиска интерфейсовна любом объекте.
Это довольно расширяемый и, как правило, самый безопасный способ пойти, когда вы не уверены.Это более многословно, чем приведенные выше решения, но вполне может справиться с неожиданными будущими изменениями.Факторинг функциональности в интерфейсах не просто, для того, чтобы почувствовать его, требуется некоторый опыт.
Шаблон посетителя
The шаблон посетителя требует большого набора текста, но он позволяет добавлять функциональность к классам без изменения этих классов.
В вашем контексте это означает, что вы можете построить свою структуру сущностей, но реализовать их действияпо отдельности.Это обычно используется, когда у вас есть очень четкие операции с вашими сущностями, вы не можете свободно изменять классы или добавление функциональности к классам сильно нарушит принцип единственной ответственности.
Это может справиться спрактически каждое требование к изменениям (при условии, что ваши сущности хорошо учтены).
(я только ссылаюсь на него, потому что большинству людей требуется время, чтобы обернуть вокруг него голову, и я бы не рекомендовал использовать его, если вы не испытали ограничений других методов)