【问题标题】:Problems designing Bejeweled game设计宝石迷阵游戏的问题
【发布时间】:2010-07-14 14:23:11
【问题描述】:

我正在尝试设计Bejeweled 游戏。我基本上有3节课。 Game 类,这是玩家将要使用的,Board 类代表棋盘,SwitchController 类负责检查棋盘上想要的开关是否有效,进行切换,计算可用切换的数量(这样我就可以知道游戏何时结束等)。

我目前的设计是这样的:

Game:
isGameOver()
isSwitchValid(coord1, coord2)
makeSwitch(coord1, coord2)
getPieceAt(coord)
getBoardLength()

IBoard:
getPieceAt(coord)
setPieceAt(coord, piece)
getLength()

我的想法是有一个ISwitchController

ISwitchController:
isSwitchValid(coord1, coord2)
makeSwitch(coord1, coord2)
getAllValidSwitches()

下面是如何组织类的小图:

我将有 2 个不同的具体类 IBoard 可供使用(对于每一个,我都必须有一个 ISwitchController 实现)。

问题:

我的程序是有 2 个 IBoard 实现:

第一个,ArrayBoard,将所有棋盘棋子存储在一个二维数组中。它没有什么特别之处。我将定义一个ArrayBoardSwitchController 来管理这个类。

第二个,ListBoard,对于每种颜色的棋子都有一个 List/Set,其中包含该颜色棋子的所有坐标。我将定义一个ListBoardSwitchController 来管理这个类。

这里的主要问题是SwitchController 的实现将在ArrayBoardListBoard 上完全不同。例如,虽然要实现getAllValidSwitches() ArrayBoardSwitchController 只需要getPieceAt() 方法,但使用ListBoardSwitchController(在那个类中我使用内部列表,因为它更容易检查移动是否是以这种方式有效)。

据我所知,有两种不同的可能解决方案:

  1. 我可以将 ISwitchControllerIBoard 接口。这样我就只有 两个班,游戏和棋盘(而 基本上游戏只是一个 董事会的控制器,因为它 将是拥有所有 游戏逻辑)。不会那么美好 因为课程不会 如果我有,他们会尽可能地有凝聚力 3 个不同的类。

  2. 让接口保持原样并放置 我需要与公共合作的所有方法 在具体的课程中。例如,如果我需要 getYellowPiecesList() 方法,我会把它公开 在ListBoard 所以ListBoardSwitchController 可以 用它。 ListBoardSwitchController 只会 知道它,因为它知道它只有效 反对ListBoards

您对此事有何看法? 这里的重点不是如何设计 Bejeweled 游戏,而是如何解决这个问题,当你尝试实现算法时会反复出现:一方面你希望有一个清晰良好的 OOP 设计,以及另一方面,有时这会妨碍健全有效的算法实现。

【问题讨论】:

  • 不相关:你用什么软件来创建那个图表?
  • @Adam:对图像 URL 的检查发现 yuml.me 是根。这是一个在线 UML 图表工具,我刚刚添加了书签并打算经常使用它。
  • @Randolpho - 很酷,你也可以购买和下载它:-)
  • @Adam:我从 Meta 上的评论中发现了那个网站,它非常酷。 :-)

标签: c# java oop


【解决方案1】:

这里的主要问题是 SwitchController 的实现在 ArrayBoard 和 ListBoard 上完全不同。

如果是这种情况,那么听起来您没有将IBoard 接口设计得足够好,以至于类可以在不知道实现细节的情况下使用IBoard 的实现。如果IBoard 的用户需要知道正在使用什么实现,那么它几乎违背了拥有接口的目的!

我强烈建议您重新访问您在IBoard 上公开的方法,看看是否有一种方法可以以更通用的方式公开诸如“在此坐标处获取作品”之类的内容。确保控制器需要在IBoard 实例上调用的任何方法IBoard 接口中的方法。

例如,虽然为了实现 getAllValidSwitches() ArrayBoardSwitchController 只需要 getPieceAt() 方法,但使用 ListBoardSwitchController(在那个类中我使用内部列表)不是一个好主意,因为它更容易检查移动是否有效那样)。

如果诸如“在此坐标处获取块”之类的操作对IBoard 接口有帮助,那么实现必须忠实于他们的合同并正确实施。听起来好像您的ListBoard 没有忠实地履行IBoard 中规定的合同。

【讨论】:

  • "如果 IBoard 的用户需要知道正在使用什么实现,那么它几乎违背了拥有接口的目的!"这是一个很好的观点。关于您的最后一点,目前两种实现都实现了“在此坐标处获取片段”。这里的问题是,例如,当想知道某个开关是否对 List 实现有效时,使用 getPieceAt() 方法不是一个好主意,因为它不会利用该实现板。
  • 那么我认为你所拥有的是一个论点,即基于列表的实现不是一个好主意,而不是直接使用特定于实现的细节的论点。也许,如果有另一种适用于列表的访问模式,您也可以将该方法添加到接口中(假设它的操作也适合基于数组的实现来实现)。
  • 基于列表的实现是最快的。这就是我有一天想要使用的实现。但它可能非常复杂,所以我现在想使用基于数组的方法来测试所有内容,然后才需要完全实现基于列表。
  • 是的,但不管interface 的目的是抽象出这些细节,以便使用它的类只需要知道允许的操作/方法是什么。无论数据如何存储,您设计界面的任务是确定“板”需要进行哪些操作。
  • 我想我找到了一个很好的解决方案。稍后我会用它更新 OP。
【解决方案2】:

3:让ArrayBoardSwitchController 和ListBoardSwitchController 成为ArrayBoard 和ListBoard 的内部类。控制器的实现与电路板的实现相关联,因此将它们放在一起是有意义的。因为控制器将是一个内部类,您可以使用板上的实现细节。然后使其工作扩展 IBoard 接口以返回 ISwitchController。

请注意,这与选项 1 略有不同。(现在可以从 IBoard 间接使用 ISwitchController,合并它们可以直接访问 ISwitchController)

【讨论】:

  • +1,尽管这确实在开关控制器和电路板之间创建了比实际需要的更紧密的耦合。如果您希望将类分开,另一种方法是定义 ListBoardSwitchController 需要的附加接口,例如 IListBoard。这是选项 (2) 的一个轻微变体,减少了耦合。
【解决方案3】:

ListBoard 作为与 ArrayBoard 分离的对象的目的是什么?如果我真的要打扰宝石列表,我会将它保存在一个对象中,该对象还包含每个位置的数组,以便可以快速有效地交换两个宝石的位置。不是说我清楚你为什么需要职位列表吗?

如果以 1.19Mhz 运行、128 字节 RAM 和 20% CPU 可用性的 6507 可以处理列,在不到 100 毫秒的时间内找到 6x20 上的所有 3-in-a-row 组合,我认为更现代的机器可以扫描在不使用宝石在哪里的列表的情况下,移动速度可以接受。我建议填充您的板阵列,这样您就不必担心边缘情况,并且对于每个宝石检查 3 个正方形内的各种单元格的 16 个组合(*),看看它们是否都匹配它。某些移动可能会被重复报告(例如,此算法可能会检测到将宝石向左移动将创建 3 行,并且还检测到将宝石移动到第一个右侧的左侧将创建 3 行-a-row) 但这应该不是问题。

(*) 如果一颗宝石可以向左移动,那么它必须匹配目标左侧的两颗宝石,或者目标上方的两颗宝石,或者下方的两颗宝石,或者一颗上方和下方的一颗。其他方向也一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    • 2023-03-13
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    相关资源
    最近更新 更多