我认为您误解了 Model-View-Controller 背后的概念。在某些时候,组件将不得不相互交互,因为它们必须在它们之间传递信息。关键是将交互与结果的实现分离。
换句话说,视图应该有一些标准接口,让模型/控制器通知它需要更新,模型/控制器应该有一些标准接口,视图用来通知用户输入,但也不应该关心对方如何处理这些事件。从理论上讲,一个完美的 MVC 实现可以让您切换任何一个组件以实现完全不同的实现,只要它坚持相同的接口并且一切都可以完全按预期工作。
在你的情况下,我会设计这样一个游戏引擎类,它具有这样的方法:
int makeMove(row, column)
其中row 和column 是棋盘上的坐标,返回的int 代表玩家(X = 0,O = 1 或类似值。您甚至可以使用enum 更明确` `) 轮到谁了,这样 GUI 就可以用该玩家的图标更新相应的方块。
然后游戏引擎会期望视图至少具有以下两种方法:
void setPlayerTurn(int player)
这告诉 GUI 它应该更新轮到它的显示器(与上面相同的player)
void setPlayerWon(int player, rows, columns)
这告诉 GUI 一个玩家获胜,并且它应该更新标识为 rows 和 columns 的三个方格作为让玩家获胜的方格,player 是相同的玩家编号以便它可以显示适当的状态消息。
游戏逻辑,包括跟踪棋盘的状态以及轮到谁,都可以独立包含在游戏类中,GUI 所做的只是将信息从用户传递到游戏,再从游戏传递给用户。从理论上讲,您可以使用完全不同的实现替换任何一个组件,该实现遵循这些接口,并且仍然可以按预期进行游戏。
因此,GUI(视图)最终会引用游戏逻辑(模型),反之亦然。但它们只是通过一个非常狭窄和通用的接口相互交互。双方都不知道也不关心对方如何处理通过该接口传递的信息。
请注意,在这个简化的示例中,我将控制器与视图结合在一起,这在您设计单一用途的 GUI 时是很常见的事情。从本质上讲,GUI 的设计使其只能用作井字游戏板,但它希望您提供自己的一套规则(模型)。
话虽如此,您可以看到,通过让 GUI 简单地公开允许您设置网格上任意正方形的状态(文本和图标)的方法,进一步解耦这些并不需要更多的时间,并且显示状态消息。它期望得到一个控制器,该控制器提供一种方法来通知用户与网格上的哪个方块进行交互。然后,控制器将提供胶水来连接通用“带有状态消息的网格”视图和游戏逻辑模型之间的接口,该模型需要我上面描述的接口。
在这种情况下,GUI(视图)获得对控制器的引用,反之亦然,然后控制器获得对游戏逻辑(模型)的引用,反之亦然。