有没有办法避免循环依赖,除了混合模块,在这样的安排(它是国际象棋应用程序)
详细说明:
Gui
模块导入ChessWidget
模块; ChessWidget
只需封装ChessWorld
模块并导入CellButton
; CellButton
模块导入模块Cell
; ChessWorld
模块导入Board
(代表它)和Players
(通知他们并获取他们的动作); Board
模块导入模块Piece
; Piece
模块导入模块Player
; 这里是问题所在:
Player
模块需要了解其他玩家和棋盘,从而导入ChessWorld
!
简短说明:
World
模块需要知道Player
模块(甚至间接地通过Board
/ Piece
)和Player
需要了解{{1} }}
非常感谢帮助。
PS:不是因为我不能使用循环依赖,而是因为它们是邪恶的。
答案 0 :(得分:15)
关注Dependency inversion principle:介绍一个ChessWorld
实现的接口,以及Player
所依赖的接口 - 和/或Player
实现的接口Piece
{{1}} 1}}取决于(取决于依赖性的细节,其中一个或两个可能是合适的)。这通常与Dependency Injection一起使用,并且,如果依赖项需要使用Factory DP动态实例化依赖项的多个实例。
答案 1 :(得分:6)
我认为循环依赖的气味显示出更多的建筑/设计问题,不应该通过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依赖于玩家模块,而不是另一个方向。
我不确定它是否完全符合您的意图,但我对此非常感兴趣,所以请发表评论。
答案 2 :(得分:3)
考虑每个对象真正需要的东西,而不是目前恰好需要的东西。
A Piece可能不需要了解播放器 - 它需要知道的是它可以发送更新的内容。
因此,对于该示例,创建一个表示“PieceMessageListener”或其他类似的接口,并让Player实现该接口。现在,两种结构都取决于抽象(遵循“结核应该取决于抽象,抽象不应该依赖于结核”的规则)。
答案 3 :(得分:0)
我要把手伸到这里说...恕我直言你可能过度设计了这个。
为什么一件作品需要掌握玩家的知识?国际象棋中的一块是黑色或白色,无论是谁控制(播放)它。
你提到“播放器模块”和“片模块” - 为什么它们是独立的模块?为什么它们只是在同一模块中的数据类(域对象)?
如果我对此进行过分析或未能理解你如何构建游戏,那么无论如何都要忽略我说的话。 OTOH也许我确实读过正确的东西?