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