Разве плохой дизайн - иметь 2 класса, которые нужны друг другу?
Я пишу небольшую игру, в которой у меня есть GameEngine
класс, в котором есть несколько GameState
объектов. Чтобы получить доступ к нескольким методам рендеринга, эти GameState
объекты также должны знать GameEngine
класс - так что это круговая зависимость.
Вы бы назвали это плохим дизайном? Я просто спрашиваю, потому что я не совсем уверен, и в настоящее время я все еще могу реорганизовать эти вещи.
c++
architecture
shad0w
источник
источник
Ответы:
Это не плохой дизайн по своей природе, но он может легко выйти из-под контроля. На самом деле вы используете этот дизайн в своих повседневных кодах. Например, вектор знает, что это первый итератор, и итераторы имеют указатель на свой контейнер.
Теперь в вашем случае гораздо лучше иметь два отдельных класса для GameEnigne и GameState. Поскольку в основном эти двое делают разные вещи, и позже вы можете определить множество классов, которые наследуют GameState (например, GameState для каждой сцены в вашей игре). И никто не может отрицать их необходимость иметь доступ друг к другу. В основном GameEngine работает с игровыми состояниями, поэтому он должен иметь указатель на них. А GameState использует ресурсы, определенные в GameEngine (например, рендеринг, менеджер физики и т. Д.).
Вы не можете и не должны объединять эти два класса друг в друга, так как они делают разные вещи по своей природе, и объединение приведет к очень большому классу, который никому не нравится.
Пока мы знаем, что нам нужна круговая зависимость в нашем дизайне. Есть несколько способов создать это безопасно:
Чтобы завершить его ответ, вы можете использовать любой из этих трех методов, но вы должны быть осторожны, чтобы не использовать один из них, так как, как я сказал, все эти проекты действительно опасны и могут легко привести к не поддерживаемому коду.
источник
Это немного запах кода , но с этим можно уйти. Если это более простой и быстрый способ запустить вашу игру, сделайте это. Но имейте это в виду, потому что есть хороший шанс, что вам придется реорганизовать его в какой-то момент.
С C ++ дело в том, что циклические зависимости не будут компилироваться так легко , поэтому лучше избавиться от них, чем тратить время на исправление компиляции.
Смотрите этот вопрос на SO для нескольких мнений.
Нет, это все же лучше, чем поместить все в один класс.
Это не так здорово, но на самом деле это довольно близко к большинству реализаций, которые я видел. Обычно у вас есть класс менеджера для состояний игры ( будьте осторожны! ) И класс рендерера, и довольно часто они являются синглетонами. Таким образом, круговая зависимость «скрыта», но потенциально она есть.
Кроме того, как вам сказали в комментариях, немного странно, что классы состояния игры выполняют какую-то визуализацию. Они должны просто хранить информацию о состоянии, а рендеринг должен обрабатываться средством визуализации или каким-либо графическим компонентом самих игровых объектов.
Теперь может быть окончательный дизайн. Мне любопытно посмотреть, принесут ли другие ответы одну хорошую идею. Тем не менее, вы, вероятно, можете найти лучший дизайн для вашей игры.
источник
Да, часто считается плохим дизайном иметь два класса, которые напрямую ссылаются друг на друга. С практической точки зрения может быть сложнее следить за потоком управления через код, владение объектами и их время жизни могут быть сложными, это означает, что ни один класс не может быть повторно использован без другого, это может означать, что поток управления должен действительно жить вне обоих из этих классов в третьем классе «посредник», и так далее.
Тем не менее, два объекта очень часто ссылаются друг на друга, и разница здесь в том, что обычно отношения в одном направлении более абстрактны. Например, в системе Model-View-Controller объект View может содержать ссылку на объект Model и будет знать все о нем, имея возможность доступа ко всем его методам и свойствам, чтобы представление могло заполняться соответствующими данными. , Объект Model может содержать ссылку на View, чтобы он мог обновлять View, когда его собственные данные изменились. Но вместо того, чтобы Модель имела ссылку на View - что сделало бы Модель зависимой от View - обычно View реализует интерфейс Observable, часто всего с 1
Update()
функция, и модель содержит ссылку на наблюдаемый объект, который может быть представлением. Когда модель изменяется, она вызываетUpdate()
все свои наблюдаемые объекты, и представление реализуетсяUpdate()
путем обратного вызова модели и извлечения любой обновленной информации. Преимущество заключается в том, что Модель вообще ничего не знает о представлениях (и зачем это нужно?) И может быть повторно использована в других ситуациях, даже в тех случаях, когда представления отсутствуют.У вас похожая ситуация в вашей игре. GameEngine обычно знает о GameStates. Но GameState не нужно знать все о GameEngine - ему просто нужен доступ к определенным методам рендеринга в GameEngine. Это должно вызвать небольшую тревогу в вашей голове, которая говорит о том, что либо (a) GameEngine пытается сделать слишком много вещей в одном классе, и / или (b) GameState не нужен весь игровой движок, только визуализируемая часть.
Три подхода к решению этой проблемы включают в себя:
источник
Обычно считается хорошей практикой иметь высокую когезию с низким сцеплением. Вот несколько ссылок, касающихся сцепления и сцепления.
Википедия связи
сплоченность википедии
низкая связь, высокая когезия
stackoverflow на лучшую практику
Наличие двух классов, ссылающихся друг на друга, имеет высокую связь. В Google Guice рамочные целей для достижения высокой когезии с низкой связью через средства инъекции зависимостей. Я бы посоветовал вам прочитать немного больше по этой теме, а затем сделать свой собственный вызов, учитывая ваш контекст.
источник