Delphi 单元没有“从根本上损坏”。它们的工作方式促进了编译器的惊人速度并促进了简洁的类设计。
能够以 Prims/.NET 所允许的方式将类分布在单元上,这种方法可以说从根本上被破坏了,因为它允许开发人员忽略正确设计其框架的需要,从而促进了类的混乱组织,促进了强加任意代码结构规则,例如“每个单元一个类”,作为通用格言没有技术或组织价值。
在这种情况下,我立即注意到由于这种循环引用困境而在类设计中出现了一种特殊性。
也就是说,为什么一个作品曾经需要参考一块板子?
如果一块棋子是从棋盘上取下来的,那么这样的参考就没有意义了,或者对于一个被移除的棋子有效的“移动目标”可能只是那些对该棋子有效的作为新游戏中的“起始位置”的那些?但我认为这只是对要求 GetMoveTargets 支持使用 NIL 板引用进行调用的案例的任意理由之外的任何理由。
单个棋子在任何给定时间的特定位置是单个国际象棋游戏的属性,同样有效可能可能的移动 任何给定的棋子都取决于其他棋子在游戏中的位置。
TChessPiece.GetMoveTargets 不需要了解当前游戏状态。这是 TChessGame 的职责。 TChessPiece 不需要参考游戏或棋盘来确定给定当前位置的有效移动目标。棋盘约束(8 个等级和文件)是域常量,而不是给定棋盘实例的属性。
因此,需要一个 TChessGame 来封装知识,该知识结合了对棋盘、棋子和 - 至关重要的是 - 规则的认识,但棋盘和棋子不需要彼此了解或游戏。
将与不同片段相关的规则放在片段类型本身的类中似乎很诱人,但恕我直言,这是一个错误,因为许多规则是基于与其他片段的交互,在某些情况下与特定片段的交互类型。这种“大局观”行为需要对整个游戏状态有一定程度的监督(阅读:概述),这在特定的棋子类别中是不合适的。
例如TChessPawn 可以确定有效的移动目标是向前一格或两格,或者如果这些对角格中的任何一个被占用,则为对角向前一格。但是,如果棋子的移动使国王处于 CHECK 状态,则棋子根本无法移动。
我会通过简单地允许 pawn 类指示所有可能的移动目标来解决这个问题 - 向前 1 或 2 个方格以及两个对角线前方方格。 TChessGame 然后通过参考这些移动目标的占用率和游戏状态来确定其中哪些是有效的。仅当兵在其本垒上时,才可能向前走 2 个方格,向前方格被占用阻止移动 = 无效目标,未占用的对角线方格促进移动,如果任何其他有效的移动暴露了国王,那么该移动也是无效的。
同样,可能会将普遍适用的规则放在基础 TChessPiece 类中(例如,给定的移动是否暴露了国王?),但应用该规则需要了解整体游戏状态- 即其他棋子的放置 - 所以它更恰当地属于 TCessGame 类的一般行为,恕我直言
除了移动目标之外,棋子还需要指明 CaptureTargets,这在大多数棋子的情况下是相同的,但在某些情况下完全不同 - pawn 就是一个很好的例子。但同样,所有潜在捕获中的任何一个 - 如果有的话 - 对于任何给定的动作都是有效的 - 恕我直言 - 对游戏规则的评估,而不是一块或一类棋子的行为。
与 99% 的此类情况 (ime - ymmv) 一样,通过更改类设计以更好地表示正在建模的问题,而不是找到将类设计硬塞到任意文件中的方法,或许可以更好地解决困境组织。