我已经思考了这个面向对象的设计问题已有一段时间了,并且无法提出令人满意的解决方案,因此我想将其向此处的人群开放以征求一些意见。
我有一个Game类,它代表一个基于回合的棋盘游戏,对于这个问题,我们可以假定它类似于Monopoly。
在我的设计中,我有一个Player类,其中包含方法TakeTurn。
游戏遍历所有玩家,并调用TakeTurn方法执行所有必要的操作以完成转弯。
我希望能够有n个播放器,并能够将任意数量的播放器设置为计算机播放器。
因此,我的想法是要拥有一个HumanPlayer类和一个ComputerPlayer类,这两个类都从Player派生而来。
游戏仅知道Player类,并且仅依次依次对每个Player调用TakeTurn方法。
我的问题在于,ComputerPlayer对象可以完全自动化,即与Monopoly示例保持一致,可以使用某种逻辑来决定购买物业。
现在,使用HumanPlayer对象,它需要从实际用户那里获得输入,以便能够购买物业,这似乎暗示着一个不同的界面,并可能意味着他们不应衍生出
在没有让Game类明确知道各种Player类的实际实现的情况下,我无法提出一个很好的解决方案。
我总是可以在Game类中做出这样的假设:只会有人类和计算机玩家,并有效地将其关闭以进行扩展,但这似乎不是一个好的OO编程。
任何意见,将不胜感激。
我认为您不应该让Game类处理IO。
这样,(阻塞)TakeTurn方法将从游戏板上隐藏实现的手段。它可以使用其他对象与用户通信。
游戏类应该关心的只是棋盘的状态和转弯。所有玩家都应实现一个Player界面,并从游戏中隐藏所有实现。
我可能没有两个HumanPlayer和ComputerPlayer类,但是只有一个Player类,该类在创建时使用正确的输入策略进行了配置。
玩家获取信息以决定其在下一轮游戏中的动作的方式是唯一变化的(至少与原始问题描述不同),因此只需将其封装在单独的抽象中即可。
无论设置游戏的任何高级班级,都应该创建两组玩家(一个人,另一台计算机模拟的),并为每个玩家提供适当的输入策略,然后将这些玩家对象简单地赋予游戏对象。然后,对于每个新回合,Game类将仅在给定的玩家列表上调用TakeTurn方法。
如果游戏正在管理游戏状态并进行I / O,则游戏做得太多。
您希望Game严格专注于规则和转弯以及状态更改。
游戏不知道玩家是什么。它只知道它有玩家。
您希望玩家检查游戏状态并在回合期间执行法律诉讼。
人类玩家和整个游戏都共享一个通用的I / O程序包,该程序包可显示游戏状态并提示人类输入。
通过将I / O包作为游戏的Observer,可以充分利用Java Observable。这样,游戏状态更改会报告给I / O进行显示或记录,或同时显示和记录两者。
Player提供给Game的接口与派生的Player类的行为正交。
TakeTurn的实现根据Player对象的具体类型而变化的事实不应引起关注。
与其告诉游戏类只有一个人,为什么不让它在游戏的菜单/初始化过程中得到输入呢?如果还有更多玩家,可以在游戏类初始化之前通过某种形式的输入(在菜单中选择玩家)来
代替游戏类在所有玩家上调用TakeTurn,玩家应在游戏类上调用TakeTurn,并且游戏类应验证是否合适的玩家轮到他。
这应该有助于解决用户和计算机播放器问题。
我会说,Game类应该不在乎这是计算机玩家还是人类玩家。它应该始终在下一个玩家类上调用TakeTurn。如果这是人类玩家,则Player类的职责是与用户交流并询问用户该怎么做。这意味着它将一直阻塞直到用户下定决心。由于通常UI交互是在应用程序的主线程中发生的,因此仅重要的一点是,阻塞的TakeTurn不会阻塞整个应用程序,否则在Game等待TakeTurn时无法处理用户输入。
我不确定这是否是您想要的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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
}
} |
我认为Game类不应该关注Player类的任何实现,也应该忽略用户界面。
任何用户输入都需要由HumanPlayer类处理。