OO设计,开放/封闭原则问题

时间:2008-09-18 09:17:30

标签: language-agnostic oop ooad open-closed-principle

我一直在思考这个面向对象的设计问题已经有一段时间了,并且无法找到一个令人满意的解决方案,所以我想在这里向人群展示一些意见。

我有一个代表基于回合制的棋盘游戏的游戏类,我们可以假设它与“垄断”类似,就此问题而言。 在我的设计中,我有一个包含方法 TakeTurn Player 类。

游戏循环遍历所有 Player 并调用TakeTurn方法完成所有必要的操作以完成转弯。 我希望能够拥有n个玩家,并能够将其中的任意数量设置为计算机玩家。 所以,我的想法是拥有一个HumanPlayer类和一个 ComputerPlayer 类,这两个类都派生自Player。

游戏只知道 Player 类,只是依次调用每个 Player 上的 TakeTurn 方法。 我的问题在于, ComputerPlayer 对象可以完全自动化,即保持Monopoly示例,可以决定使用某种逻辑购买属性。 现在,使用 HumanPlayer 对象,它需要从实际用户那里获得一个输入,以便能够购买一个属性,这似乎意味着一个不同的接口,并可能意味着他们不应该派生< / p>

如果没有让 Game 类明确知道各种 Player 类的实际实现,我就无法找到解决问题的好方法。 我总是可以在 Game 类中做出这样的假设,那就是只有人类和计算机玩家并且有效地关闭它以进行扩展,但它看起来不像是好的OO编程。

对此有任何意见,我们将不胜感激。

9 个答案:

答案 0 :(得分:7)

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

所有游戏类应关注的是棋盘状态和转弯状态。玩家都应该实现单个玩家界面,并隐藏游戏中的所有实现。

答案 1 :(得分:2)

如果游戏正在管理游戏状态进行I / O,那么游戏就会做得太多。

您希望Game专注于规则和转弯以及状态变化。 游戏不知道玩家是什么;它只知道它有玩家。

您希望玩家检查游戏状态并在轮流期间执行法律行动。

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

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

答案 2 :(得分:2)

我可能没有两个HumanPlayerComputerPlayer类,而是一个Player类,它在创建时使用正确的输入策略进行配置。

玩家在游戏的下一轮获取信息以决定其移动的方式是唯一的事物(至少从原始问题描述中),所以只需将其封装在一个单独抽象。

无论设置游戏的高级班级还应创建两组玩家(一组是人类,另一组是计算机模拟),每个玩家都有适当的输入策略,然后简单地将这些玩家对象赋予游戏对象。然后,对于每个新的回合,Game类将仅在给定的玩家列表上调用TakeTurn方法。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

Player 提供给 Game 的界面与派生的 Player 类的行为正交。

TakeTurn 的实现因 Player 对象的具体类型而异的事实不应引起关注。

答案 5 :(得分:0)

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

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

答案 6 :(得分: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
  }
}

答案 7 :(得分:0)

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

答案 8 :(得分:0)

而不是游戏类在所有玩家上调用 TakeTurn 而不是 TakeTurn 游戏课程游戏课程应该验证合适的玩家是否轮到他了。

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