作为一个夏季的项目,当我从Uni有一些停机时间我将建立一个垄断游戏。这个问题更多的是关于问题的一般概念,而不是我正在尝试执行的具体任务。
我决定采用自下而上的方法来构建它,在四十个空间板周围创建运动,然后继续与空间交互。我意识到我完全不确定如何处理这个问题,而且我在两个设计理念之间徘徊:
为每个空间提供自己的对象,空间对象的所有子类,以便空间对象本身可以定义交互。我可以通过为每种类型的空间实现不同的land()方法来实现这一点。
只提供Properties和Utilities(因为每个属性都有独特的功能)对象,并创建方法来处理程序主类(或我称之为Board)中的购买/租赁等。像go和super tax这样的空间可以通过一小组条件来实现,以检查玩家是否在一个特殊的空间。
选项1显然是OO(我认为是正确的)做事方式,但我只想处理来自程序主类的用户交互。换句话说,我不希望空间物体与玩家交互。 为什么? Errr。到目前为止,我所做的很多编码都具有这种简单性,但我不确定这对于大型项目是否是一个梦想。我真的应该在一个完全独立的类中处理用户交互吗?
你可以看到我对这种情况很困惑。这有什么办法吗?并且,有没有人对实际的OO设计有任何建议可以帮助一般?
编辑:请注意我觉得我对这个问题失去了一点关注。我对组合OO和任何外部操作(命令行,网络,GUI,文件管理等)的一般方法感兴趣。
答案 0 :(得分:5)
我同意选项#1似乎更好。
至于“用户互动” - 一切都取决于。您可以将一些代码留在另一个类中。例如,
// in main class
user.landOn(space);
if (space.containsProperties()) doSomething(); // Option #1 for some user-interaction code
// in User.java
public void landOn(Space s) {
// do some checks
s.land(this);
if (s.containsProperties()) {...} // Option #2
// something else?
}
// in GetMoneySpace.java
@Override
public void land(User u) {
u.awardCash(200);
// Option #3 - no properties so nothing here
}
这比其他类似的东西(在我看来更好)(
)if (space.isCashAwardSpace()) {
user.awardCash(space.getAward());
}
if (user.something()) doSomething(); // Some user-interaction code
答案 1 :(得分:5)
最后,这取决于你。这是OO的美丽,因为它需要解释。通常应该遵循一些模式,但一般来说,您决定如何处理它。
但是,你应该仔细考虑系统中每个参与者应该知道的其余部分。财产应该真正了解玩家,他的账户余额以及其他玩家吗?可能不是。物业应该知道它的成本,租金是多少等等。
另一方面,主要播放线程是否应关注支付租金等微不足道的事情?可能不是。其主要关注点应该是游戏本身的状态,例如骰子滚动,每个玩家是想交易还是购买还是非抵押/抵押等等。
想一想在广场上降落的动作。登陆后,玩家有3种选择:
现在,系统中的哪个actor知道完成该操作所需的所有信息。我们有Game类,它不关心这种单调乏味。我们拥有该物业,并不真正关心球员。但是Player对象知道所有这些信息。它记录每个玩家拥有的内容,并可以轻松访问正确的数据。
所以,如果是我,我会制作一个Player.performMove(Die d)方法。它可以轻松访问帐户。这也允许类之间的耦合最少。
但最终,这取决于你。我相信人们已经用完美的OO以及功能或程序语言创建了Monopoly克隆。最后,使用你所知道的并继续重构,直到你对最终设计感到满意为止。
答案 2 :(得分:1)
选择第一个设计。您将拥有一个Property类,并对特殊属性进行子类化,从而覆盖默认行为。
就交互而言,您可以拥有一个Token类,并在棋盘周围移动一个实例。您必须为用户提供一些选项,但是,从响应中,您应该调用对象上的方法,而不是在用户事件中放置复杂的逻辑。
示例类:
答案 3 :(得分:1)
我不完全确定我是否理解正确。在设计软件时,您始终有这样的选择。我个人会选择第一个选择。一个论点是小游戏(Scrabble)的个人经验,这让我觉得好的设计也适用于小型项目。 OOP的重点在于,您可以对您的设计有不同的看法,并获得一些设计优势。例如,想象一下添加新字段,更改现有字段,重用多个字段中一个字段的行为是多么困难。
答案 4 :(得分:1)
你的第一个方法是我要去的方法。它将行为封装在需要的地方。因此,你有实用程序,属性,GotoJail,FreeParking的Space子类 - 基本上所有不同的空间类别。对类别进行分组的是它的常见行为。
您的属性空间本身可能有一个组对象作为成员,例如将所有深蓝色属性组合在一起。
关于与用户的交互,您将Board(或更好的GameController)实例传递给每个空间,因此它知道它所属的游戏并且可以影响游戏。然后,Space可以在棋盘上调用特定的动作,例如,移动棋子,向用户询问问题等。主要观点是存在分离 - 用户交互不在每个空间内发生 - 但允许空间要求发生一些交互,或者移动一块。这取决于您的GameController实际进行交互或移动棋子等。这种分离使其易于测试,并根据需要提供替代实施(例如,不同版本/国家/地区的不同游戏规则?)
答案 5 :(得分:0)
选项2没有多大意义,或者至少它不像选项1那样清晰。对于选项1,您不需要 来处理空间对象内的用户交互。您可以在主类或专用于处理用户交互的单独类中使用:
public void move(Player p, int spaces){
Space landingSpace = board.getLandingSpace(p,spaces);
landingSpace.land(p); //apply your logic here
}
如您所见,Space
类负责检查打算在该空间上着陆的Player p
。它应用任何自定义逻辑,检查它是否有足够的钱,如果它是玩家拥有的东西等等。Space
的每个子类都有自己的一套规则,如选项1中所述。
答案 6 :(得分:0)
面向对象设计的部分要点是简化解决方案空间内问题的表示(即,在计算机中对系统进行建模)。在这种情况下,请考虑对象之间的关系。 Space
中是否有足够的功能来保证将其抽象为一个类,或者是否因为存在与Property
无关的离散Utility
和Space
类更有意义,因为两者的独特功能? Property
是Space
的一种特殊类型,还是Space
中的一个字段?这些是你在设计游戏时可能需要解决的问题。
就交互而言,当你有一个'god class'完成所有工作并且只是向其他类提供信息时,对于一个设计来说通常是坏消息。有很多方法可以陷入这个陷阱;确定您是否正在处理神班的一种方法是查找包括Manager
或System
的班级名称。因此,拥有某种“游戏管理器”可能不是最好的想法,它会向所有其他对象询问数据,进行所有更改并跟踪所有内容。尽可能地消除这些。
上帝阶级违反了encapsulation的概念,它涉及的不仅仅是数据隐藏(尽管这肯定是其中很重要的一部分)。良好的封装意味着相关的方法和数据是单个对象的一部分。例如,属性不需要向其所有者发出请求,因此包含对Player
的引用的字段可能违反封装。其中一些封装违规行为根本不明显,很难发现。在设计对象时,尝试确定有关需要与外部对象共享的对象的最小信息量。修剪任何不必要的东西。
显然你可以通过很多方式解决这个问题,但我的设计会是这样的(迭代肯定会证明它错了):
Space
类,包含所有空格共有的基本数据和方法(例如它们在板上的位置,是否占用等)。Property
和Utility
)移动到最独特的(Go
,Jail
,FreeParking
等等;可能是单身人士)与每个相关的字段和方法。Player
类包含玩家信息。GameState
关注游戏状态的课程;轮到它了,银行里还剩下多少房子,等等。祝你好运,继续学习。
当然,谷歌是你的朋友,但这里有一些我建议读的东西:
ATM simulation(这个想法是 丽贝卡也讨论过 Wirfs-Brock的书如下)
Object-Oriented Design Heuristics - Arthur Riel