OO设计,开闭原则问题

7

我已经思考了一段时间关于面向对象设计的问题,但一直没有想出一个令人满意的解决方案,因此想请大家给予意见。

我有一个代表回合制棋盘游戏的Game类,我们可以假设它类似于大富翁。 在我的设计中,我有一个包含TakeTurn方法的Player类。

Game循环遍历所有Player并调用TakeTurn方法来完成回合所需的所有操作。 我希望能够拥有n个玩家,并且能够设置任意数量的计算机玩家。 因此,我的想法是有一个HumanPlayer类和一个ComputerPlayer类,两者都派生自Player

Game只知道Player类,并简单地依次调用每个PlayerTakeTurn方法。 我的问题在于ComputerPlayer对象可以完全自动化,即保持与大富翁示例相同,可以使用某些逻辑来决定购买一块地产。 现在,对于HumanPlayer对象,它需要从实际用户那里获取输入,以便例如购买地产,这似乎意味着不同的接口,并且可能意味着它们不应该派生

我没有想出一个好的解决方案来解决这个问题,而不必让Game类显式地知道各种Player类的实际实现。 我可以在Game类中做出假设,即只会有人类和计算机玩家,从而有效地关闭其扩展性,但这似乎不是良好的面向对象编程。

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


我不确定这里是否适用于[标签:开闭原则]。 - Fuhrmanator
9个回答

7
我认为你不应该让Game类处理IO。这样,(阻塞的)TakeTurn方法将会隐藏游戏板的实现方式。它可以使用其他对象与用户进行通信。
Game类应该关注的是棋盘的状态和回合。所有玩家都应该实现一个单一的Player接口,并将所有实现从Game中隐藏。

1
为了使游戏具有良好的“内聚性”(适当的责任),努力争取+1。 - Fuhrmanator

2

我可能不会使用两个HumanPlayerComputerPlayer类,而是使用一个单独的Player类,该类在创建时配置适当的输入策略。

玩家在决定下一轮游戏动作之前获取信息的方式是唯一变化的事情(至少从原始问题描述中来看),因此只需将其封装在单独的抽象中。

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


2
如果游戏管理游戏状态并进行I/O操作,那么游戏就做得太多了。
你希望游戏专注于规则、回合和状态变化。游戏不知道什么是玩家,它只知道有玩家。
你希望玩家检查游戏状态并在他们的回合中执行合法的动作。
人类玩家和整个游戏都共享一个常见的I/O包,该包显示游戏状态并提示人类输入。
您可以通过将I/O包设置为游戏的观察者来充分利用Java Observable。这样,游戏状态更改会报告给I/O进行显示、记录或两者兼备。

1

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


1

PlayerGame 展示的接口与派生的 Player 类的行为正交。

TakeTurn 的实现因 Player 对象的具体类型而异,这一事实不应引起担忧。


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
  }
}

0

我认为Game类不应该关注任何玩家类的实现,也不应该考虑用户界面。

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


0
我认为,Game类不应该关心这是计算机玩家还是人类玩家。它应该始终在下一个玩家类上调用TakeTurn。如果这是一个人类玩家,则由Player类负责与用户通信并询问用户要做什么。这意味着它将阻塞直到用户做出决定。由于通常UI交互发生在应用程序的主线程中,因此只有阻塞的TakeTurn不会阻塞整个应用程序,否则当Game等待TakeTurn时无法处理用户输入。

0

游戏类不应该在所有玩家上调用TakeTurn,而是应该由玩家调用TakeTurnGame类上,Game类应该验证是否正确的玩家正在进行他的回合。

这应该有助于解决用户计算机玩家问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接