我一直在思考这个面向对象的设计问题已经有一段时间了,并且无法找到一个令人满意的解决方案,所以我想在这里向人群展示一些意见。
我有一个代表基于回合制的棋盘游戏的游戏类,我们可以假设它与“垄断”类似,就此问题而言。 在我的设计中,我有一个包含方法 TakeTurn 的 Player 类。
游戏循环遍历所有 Player 并调用TakeTurn方法完成所有必要的操作以完成转弯。 我希望能够拥有n个玩家,并能够将其中的任意数量设置为计算机玩家。 所以,我的想法是拥有一个HumanPlayer类和一个 ComputerPlayer 类,这两个类都派生自Player。
游戏只知道 Player 类,只是依次调用每个 Player 上的 TakeTurn 方法。 我的问题在于, ComputerPlayer 对象可以完全自动化,即保持Monopoly示例,可以决定使用某种逻辑购买属性。 现在,使用 HumanPlayer 对象,它需要从实际用户那里获得一个输入,以便能够购买一个属性,这似乎意味着一个不同的接口,并可能意味着他们不应该派生< / p>
如果没有让 Game 类明确知道各种 Player 类的实际实现,我就无法找到解决问题的好方法。 我总是可以在 Game 类中做出这样的假设,那就是只有人类和计算机玩家并且有效地关闭它以进行扩展,但它看起来不像是好的OO编程。
对此有任何意见,我们将不胜感激。
答案 0 :(得分:7)
我认为你不应该让Game类处理IO。 这样,(阻塞)TakeTurn方法将隐藏游戏板的实现手段。它可以使用其他对象与用户进行通信。
所有游戏类应关注的是棋盘状态和转弯状态。玩家都应该实现单个玩家界面,并隐藏游戏中的所有实现。
答案 1 :(得分:2)
如果游戏正在管理游戏状态和进行I / O,那么游戏就会做得太多。
您希望Game专注于规则和转弯以及状态变化。 游戏不知道玩家是什么;它只知道它有玩家。
您希望玩家检查游戏状态并在轮流期间执行法律行动。
人类玩家和整个游戏都共享一个通用的I / O包,显示游戏状态并提示人们输入。
通过使I / O包成为游戏的Observable
,您可以充分利用Java Observer
。这样,游戏状态更改将报告给I / O以进行显示或记录或两者兼而有之。
答案 2 :(得分:2)
我可能没有两个HumanPlayer
和ComputerPlayer
类,而是一个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 游戏课程,游戏课程应该验证合适的玩家是否轮到他了。
这有助于解决用户和计算机播放器问题。