Игра C ++ - сигнализация родительского класса, проблема циклической зависимости - PullRequest
1 голос
/ 19 сентября 2010

У меня небольшая проблема с круговой зависимостью.Это работает хорошо, но это делает для уродливого кода.Это в контексте игры Snake.

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

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

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

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

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

#include "SnakeSegment.hpp"

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

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

Ответы [ 2 ]

4 голосов
/ 19 сентября 2010

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

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

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

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

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

3 голосов
/ 19 сентября 2010

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

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

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

...