Как избежать круговых зависимостей здесь
Есть ли способ избежать круговых зависимостей, кроме смешивания модулей, в такой схеме (это шахматное приложение)
Длинное описание:
- Существует модуль
Gui
, который импортирует модуль ChessWidget
;
-
ChessWidget
просто обертывает модуль ChessWorld
и импортирует CellButton
;
- Модуль
CellButton
импортирует модуль Cell
;
- Модуль
ChessWorld
импортирует Board
(для его представления) и Players
(чтобы уведомить их и получить их ходы);
- Модуль модуля
Board
импортирует модуль Piece
;
- Модуль модуля
Piece
импортирует модуль Player
;
И ЗДЕСЬ ПРОБЛЕМА:
Модуль Player
должен знать о других игроках и плате, импортируя ChessWorld
!
Краткое описание:
Модуль World
должен знать о модуле Player
(даже косвенно Board
/Piece
) и Player
должен знать о World
.
Помощь очень приветствуется.
PS: Не потому, что я не могу использовать круговые зависимости, а потому, что они злы.
Ответы
Ответ 1
Следуйте Принцип инверсии зависимостей: введите интерфейс, который ChessWorld
реализует и на котором Player
зависит - и/или один из которых Player
реализует и на котором Piece
зависит (оба или оба могут быть подходящими в зависимости от деталей о характере зависимости). Это часто сопровождается Injection of Dependency, и если зависимому нужно динамически создавать экземпляр экземпляра зависимых лиц, Factory DPs.
Ответ 2
Я думаю, что запах круговой зависимости показывает больше проблемы архитектуры/дизайна, которые не должны решаться DI, поздним ограничением, свободной связью или любой другой формой дополнительного уровня абстракции. Хотя они все очень хорошие механизмы, но не решают проблему ниже.
Короче: я думаю, что ChessWorld содержит слишком много обязанностей. Если вы разделите их, вы, вероятно, обнаружите, что зависимости - это обязанности, которые лучше подходят для отдельного модуля.
Длительное объяснение: я попытаюсь привести пример того, как я буду реорганизовывать его, хотя это сложно, потому что я действительно не являюсь теперь полным проблемным доменом.
ПРИМЕЧАНИЕ. Я не знаком с Java, поэтому я могу неправильно понять последствия импорта и переноса.
Но, как я понимаю, зависимости выглядят примерно так:
Gui <- ChessWidget <- ChessWorld <- CellButton <- Cell
<- Board <- Piece <- Player
<- Players <- ChessWorld
ИМХО проблема заключается в том, что ChessWorld выполняет слишком много разных обязанностей. Поддержание списка игроков, вероятно, лучше в отдельном модуле, таком как PlayerList, RegisteredUsers или OnlineUsers или что-то подобное. После этого рефакторинг будет изменяться следующим образом:
Gui <- ChessWidget <- ChessWorld <- CellButton <- Cell
<- Board <- Piece <- Player
<- Playerlist <- Player
PlayerList - это, вероятно, то, что у вас было бы в модуле плеера. Теперь Chessworld зависит от модуля игрока, а не в другом направлении.
Я не уверен, что это соответствует вашему намерению, но мне очень интересно обсудить это, поэтому, пожалуйста, прокомментируйте.
Ответ 3
Рассмотрим, что нужно каждому объекту, а не то, что ему сейчас нужно.
Piece, вероятно, не нужно знать о игроке - о чем он должен знать, это то, что он может отправлять обновления.
Итак, для этого примера создайте интерфейс, представляющий "PieceMessageListener" или некоторые такие, и попросите Player реализовать это. Теперь обе конкреции зависят от абстракции (переход к правилу "конкреции должен зависеть от абстракций, абстракции не должны зависеть от конкреций" ).
Ответ 4
Я задержу свою руку здесь и скажу... ИМХО, вы, возможно, слишком разработали это.
Почему кусок должен иметь знания об игроке? Часть в шахматах либо черная, либо белая, независимо от того, кто контролирует (играет) ее.
Вы упомянули "модуль игрока" и "кусочный модуль" - почему они являются отдельными модулями? Почему они не просто классы данных (объекты домена) вместе в одном модуле?
Если я уже проанализировал это или не понял, как вы построили свою игру, то непременно проигнорируйте сказанное. OTOH, может быть, я правильно прочитал?