7

我一直在考虑这个面向对象的设计问题,但无法提出令人满意的解决方案,所以我想在这里向人群开放以征求意见。

我有一个代表回合制棋盘游戏的Game类,出于这个问题的目的,我们可以假设它类似于 Monopoly。在我的设计中,我有一个包含方法TakeTurn的Player类。

Game循环遍历所有Player并调用 TakeTurn 方法来完成所有必要的事情以完成转弯。我希望能够拥有 n 个玩家,并且能够将任意数量的玩家设置为电脑玩家。所以,我的想法是有一个 HumanPlayer 类和一个ComputerPlayer类,它们都派生自 Player。

Game只知道Player类,并简单地依次调用每个Player的TakeTurn方法。我的问题在于,ComputerPlayer对象可以完全自动化,即与 Monopoly 示例保持一致,可以使用某种逻辑决定购买房产。现在,对于HumanPlayer对象,它需要从实际用户那里获得输入才能购买房产,例如,这似乎暗示了不同的界面,并且可能意味着它们不应该派生

如果不让Game类明确知道各种Player类的实际实现,我就无法为这个问题提出一个好的解决方案。我总是可以在Game类中假设永远只有人类和计算机玩家并有效地关闭它以进行扩展,但这似乎不是好的 OO 编程。

对此的任何意见将不胜感激。

4

9 回答 9

7

我认为你不应该让 Game 类处理 IO。这样,(阻塞的)TakeTurn 方法将从游戏板中隐藏执行方式。它可以使用其他对象与用户进行通信。

所有 Game 类都应该关注的是棋盘和转牌的状态。玩家应该都实现一个单一的 Player 接口,并从游戏中隐藏所有实现。

于 2008-09-18T09:21:29.937 回答
2

如果游戏正在管理游戏状态进行 I/O,则游戏做的太多了。

您希望 Game 只关注规则、回合和状态变化。游戏不知道玩家是什么;它只知道它有玩家。

您希望玩家检查游戏状态并在轮到他们时执行法律行动。

人类玩家和整个游戏共享一个通用 I/O 包,该包显示游戏状态并提示人类输入。

Observable您可以通过将 I/O 包作为Observer游戏的一部分来充分利用 Java 。这样,游戏状态更改会报告给 I/O 以进行显示或记录或两者兼而有之。

于 2008-09-20T20:29:18.480 回答
2

我可能不会有两个HumanPlayer和类,而是在创建时使用正确的输入策略配置ComputerPlayer的单个类。Player

玩家获取信息以决定其在下一轮游戏中的移动的方式是唯一不同的东西(至少与最初的问题描述不同),因此只需将其封装在一个单独的抽象中。

任何设置游戏的高级类也应该创建两组玩家(一个是人类,另一个是计算机模拟的),每个玩家都有适当的输入策略,然后简单地将这些玩家对象提供给游戏对象。然后,Game 类将只TakeTurn为每个新回合调用给定玩家列表上的方法。

于 2009-09-14T16:32:44.193 回答
1

与其告诉游戏类只有一个人,不如让它在游戏的菜单/初始化过程中获得输入?如果有更多玩家,可以在游戏类初始化之前通过某种形式的输入(在菜单中选择玩家)来决定。

于 2008-09-18T09:22:10.370 回答
1

Player呈现给Game的接口与派生的Player类的行为是正交的。

TakeTurn的实现取决于Player对象的具体类型这一事实不应引起关注。

于 2008-09-18T09:25:10.707 回答
0

我认为GameClass 不应该关心 Player 类的任何实现,也应该忽略用户界面。

任何用户输入都需要由HumanPlayer该类处理。

于 2008-09-18T09:23:21.290 回答
0

我不确定这是否是你想要的

public abstract class Player 
{
  int position;
  DecisionMaker decisionDependency;

  ...

  public void TakeTurn()
  {
    position += RollDice();
    GameOption option GetOptions(position);
    MakeDescion(option);
  }

  protected int RollDice()
  {
    //do something to get the movement
  }

  protected abstract void MakeDecision(GameOption option);

}

Public class ComputerPlayer : Player
{
  public ComputerPlayer()
  {
    decisionDependency = new AIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably delgate toan AI based dependency
  }
}

Public class HumanPlayer : Player
{
  public HumanPlayer()
  {
    decisionDependency = new UIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably interacting with the a UI or delgate to a dependency
  }
}
于 2008-09-18T09:41:20.840 回答
0

我想说,Game类不应该关心这是计算机玩家还是人类玩家。它应该总是在下一个播放器类上调用TakeTurn 。如果这是一个人类玩家,那么Player类的职责就是与用户交流并询问用户要做什么。这意味着它将阻止直到用户下定决心。由于 UI 交互通常发生在应用程序的主线程中,因此唯一重要的是阻塞的TakeTurn不会阻塞整个应用程序,否则在Game等待TakeTurn时无法处理用户输入。

于 2008-09-18T10:09:05.983 回答
0

玩家应该在Game类上调用TakeTurn ,而不是Game类对所有玩家调用TakeTurn,而Game应该验证是否轮到了正确的玩家。

这应该有助于解决用户计算机播放器问题。

于 2009-03-10T07:25:09.557 回答