Привет всем!
Есть такая иерархия классов(это уже почти игра):
// Абстрактный класс поведения class Behaviour { public: ... virtual void Do(GameObject* pObject) = 0; ... }; // Абстрактный класс для игрового объекта class GameObject { public: ... virtual void Update( ) = 0; ... protected: Behaviour* m_pBehaviour; }; // Допустим, есть класс Ниндзя :) class Ninja: public GameObject ... // А также классы поведений для разных "ниньзь" :) class RedNinjaBehaviour: public Behaviour ... class WhiteNinjaBehaviour: public Behaviour ... class BlackNinjaBehaviour: public Behaviour ...
Каждый раз, переопределяя метод Do(), я пишу следующее:
void Do(GameObject* pObject) { Ninja* pNinja = ( Ninja*)pObject; // Провожу преобразование (!!!) // а дальше что-то делаем ... }
Мне офигенно не нравится такая идеология из-за того, что приходится каждый раз приводить преобразование в нужный класс. Но это лучшее, что я смог придумать. Вопрос: как можно организовать это все иначе; или (на крайняк) избавиться от преобразования?
Спасибо за осиливание многабукаф
GameObject владеет Behaviour или нет? Если владеет, то вот такой вариант:
Просто убрать параметр у Do, а GameObject передавать в констукторе Behaviour.
// этот класс определяет какое-то общее не специфичное поведение class SomeGenericGameObjectBehaviour : public Behaviour { protected: GameObject* m_gameObject; public: SomeGenericGameObjectBehaviour(GameObject* ); }; // этому поведению обучены только красные ниндзи class RedNinjaBehaviour : public Behaviour { protected: Ninja* m_ninja; public: RedNinjaBehaviour( Ninja* ); void Do( ) { m_ninja->... } };
потом Ninja сможет управлятся либо RedNinjaBehaviour либо SomeGenericGameObjectBehaviour.
gexogen
Ух ты, как просто! И переделывать ничего особо не нужно. Спасибо большое :)
gexogen
Рано обрадовался.
Получается, что я не смогу поиметь две красные ниндзи с одним поведением, поскольку поведение хранит указатель на одного из них. (Или нужно создать два одинаковых поведения, а это как-то не очень...)
Проблема остается...
but-cher
> Каждый раз, переопределяя метод Do(), я пишу следующее:
> void Do(GameObject* pObject)
> {
> Ninja* pNinja = (Ninja*)pObject; // Провожу преобразование (!!!)
>
> // а дальше что-то делаем
> ...
> }
В паскале можно примерно так, может и в C такое подобное есть:
procedure Do(pObject: GameObject); var Obj: Ninja absolute pObject; begin // а дальше что-то делаем с Obj как с переменной pObject, но уже типа Ninja end;
Я так понимаю это почти решает твою проблемму, т.е. надо написать всего одну дополнительную строчку, это уже терпимо.
but-cher
> Получается, что я не смогу поиметь две красные ниндзи с одним поведением
Ninja n1 , n2; RedNinjaBehaviour rnb1(n1) , rnb2( n2);
Нэ?
louken
Я подозреваю, что в паскале происходит тоже преобразование типов. Если это не так, и это решает проблему в паскале, то, насколько я знаю, в C++ такого нет (кроме уже предложенного).
А написать дополнительную строчку вообще не проблема, лишь бы эта дополнительная строчка не обрабатывалась каждый цикл рендера по несколько сотен раз. Есть мнение, что это экономия на спичках :)
Merrewend
Лучше когда одно поведение на много ниндзей, чем много поведений на много ниндзей... Ты же не загружаешь каждый раз одну и ту же текстуру для новой модели из-за кривой архитектуры?
класс Ninja - это данные; класс Behaviour - это то, что меняет данные заданным образом (вообще все задумывалось для того, чтобы не лезть в потроха каждый раз для добавления нового поведения). Похоже сама парадигма гнилая и нужно думать в другую сторону.
С-каст дальше static cast не пойдет. да и pNinja если и вообще создастся,то как DWORD в стеке. Не вижу смысла это оптимизировать дальше
but-cher
> Лучше когда одно поведение на много ниндзей, чем много поведений на много
> ниндзей... Ты же не загружаешь каждый раз одну и ту же текстуру для новой
> модели из-за кривой архитектуры?
Я, если честно, вообще не понимаю чем плох вариант 0-го поста. Даункаст, конечно, не есть гуд, но и не трагедия мирового масштаба. В крайнем случае используй dynamic_cast в дебаге.
Тема в архиве.