2010-09-19 3 views
1

У меня проблема с круговой зависимостью. Он отлично работает, но он делает для уродливого кода. Это в контексте игры Снейка.C++ Game - сигнализация родительского класса, проблема с круговой зависимостью

У меня есть класс Snake, который содержит вектор SnakeSegments и управляет их взаимодействием (например, перемещение и рост как единицы, а не как отдельные объекты).

Когда SnakeSegment сталкивается с объектом Food, он переворачивает свой член hasEaten в true. Змея регулярно запрашивает SnakeSegments, по существу для этого участника. Если какой-либо из запросов возвращает положительный результат (т. Е. Один попал в пищу), тогда Змея будет расти как единица (т. Е. Развернуть голову и сжать хвост). Это все хорошо и хорошо, но я бы предпочел более основанный на сигналах подход, когда, когда SnakeSegment попадает в пищу, он отправляет предупреждение (сигнал, прерывание и т. Д.) В класс Snake, который говорит, что он растет , Это означает, что у меня не было бы уродливого кода в моей функции обновления Snake, проверяя все сегменты; и вместо этого у меня будет функция OnEat() в моем классе Snake.

Однако это приводит к круговым зависимостям; Змея содержит вектор SnakeSegments, а SnakeSegments имеет Snake & или Snake *, который сообщает им, кого предупредить, когда они едят. В коде, я в основном просто должны предварительно объявить класс Snake:

class Snake; 
class SnakeSegment 
{ 
... 
Snake* alertOnEat; 
... 
};

и мой класс Snake просто работает нормально

#include "SnakeSegment.hpp" 

class Snake 
{ 
... 
std::vector segments; 
... 
void OnEat(); 
... 
};

Есть ли более хорошие проекты в этом? Обратите внимание, что здесь не просто проблема; аналогичная проблема возникает в ряде областей (например, GameWorld содержит члена Snake, а Змея предупреждает GameWorld, когда он умирает), поэтому решение, специфичное для Snake и SnakeSegment, не является тем, что я ищу.

ответ

3

Возможно, вы захотите рассмотреть эту проблему - шаблон проектирования Observer/Observable. Он позволяет создавать объекты, которые наблюдают (Snake) наблюдаемые объекты (SnakeSegment), и получать уведомления сразу же, когда их состояние изменилось.

Wikipedia имеет хороший пример, написанный на многих языках, включая C++.

Это общий шаблон, используемый во многих графических интерфейсах, поэтому слой «Вид» может измениться при изменении любой из данных модели.

4

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

Однако вы должны ограничить эти зависимости между интерфейсами или базовыми классами, а не непосредственно производными классами. Хорошим примером является зависимость от мира змей. Вероятно, вы не обязательно хотите, чтобы класс World знал обо всех возможных типах игровых объектов. Тем не менее, вы можете сделать все ваши игровые объекты из общего класса GameObject и сделать World и GameObject взаимозависимыми.

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

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

Никогда не забывайте, что дизайн компромисс.

+0

Не могли бы вы объяснить, почему лучше создавать зависимости между базовыми классами? – problemofficer

+0

Целью является сокращение зависимостей. Если вы связываете только базовые классы (World и GameObject), вам не нужно зависеть от всех возможных подклассов GameObject. Экземпляр Fior, если создать Ennemy: GameObject, я могу легко добавить его в мир как GameObject, не подозревая, что этот подкласс существует. – Coincoin

Смежные вопросы