从简单开始,我有两个对象 - 玩家和游戏:
Game, Player
玩家将其动作发送到游戏,游戏处理它们并更新玩家的状态(以及游戏中的所有其他玩家):
Player.do_move_X:
game.handle_move_X()
Game.handle_move_X:
player.take_damage(10)
接下来,我添加了多种游戏类型(游戏的子类):
GameNormal, GameCaptureTheFlag, GamePractice
通过简单地重新实现子类中的相关方法,可以很容易地改变不同Game类之间的行为:
GamePractice.handle_move_X:
player.take_damage(5)
接下来,我添加了多个Player子类:
PlayerNormal, PlayerUnregistered, PlayerElite
现在它变得棘手,因为玩家和游戏之间的互动取决于玩家在游戏类型上的类型和。现在我最终得到的代码如下:
GamePractice.handle_move_X:
if player is a PlayerNormal:
player.take_damage(5)
else if player is a PlayerUnregistered:
player.take_damage(5)
else if player is a PlayerElite:
player.take_damage(10)
(类似地,我可以将这种逻辑放在Player类而不是Game类中。)
检查big switch或if-else语句中的对象类型是否很难看。我有一个选择是向Player类添加显式方法来处理每种游戏类型的特定情况:
GameNormal.handle_move_X:
player.take_normal_damage()
PlayerNormal.take_normal_damage:
take_damage(10)
PlayerElite.take_normal_damage:
take_damage(15)
GamePractice.handle_move_X:
player.take_practice_damage()
PlayerNormal.take_practice_damage:
take_damage(5)
PlayerElite.take_practice_damage:
take_damage(10)
这消除了上面的丑陋,但增加了一个新的丑陋 - 它使用公共方法使Player类膨胀,以处理每种游戏类型的每个案例。
有没有更好的范例或设计来处理这种情况?
答案 0 :(得分:1)
我不知道它是否是最好的方法,但我认为visitor pattern可以帮到这里。实现取决于语言,但基本上你有一个Visitor
接口,visit
方法和Player
及其每个子类。然后,对于Player
树中的每个类,如果accept
是接口/抽象,那么您将使用Player
方法,或者仅用于子类:
PlayerNormal.accept(Visitor v):
v.visit(this)
PlayerUnregistered.accept(Visitor v):
v.visit(this)
PlayerElite.accept(Visitor v):
v.visit(this)
最后,您需要为Visitor
及其每个子类实现Game
- 再次,可能只是针对子类。这可能是私人课程:
GameNormal.TakeDamageVisitor.visit(PlayerNormal p):
p.take_damage(10)
GameNormal.TakeDamageVisitor.visit(PlayerUnregistered p):
p.take_damage(10)
GameNormal.TakeDamageVisitor.visit(PlayerElite p):
p.take_damage(15)
GameCaptureTheFlag.TakeDamageVisitor.visit(PlayerNormal p):
p.take_damage(10)
GameCaptureTheFlag.TakeDamageVisitor.visit(PlayerUnregistered p):
p.take_damage(15)
GameCaptureTheFlag.TakeDamageVisitor.visit(PlayerElite p):
p.take_damage(15)
GamePractice.TakeDamageVisitor.visit(PlayerNormal p):
p.take_damage(5)
GamePractice.TakeDamageVisitor.visit(PlayerUnregistered p):
p.take_damage(5)
GamePractice.TakeDamageVisitor.visit(PlayerElite p):
p.take_damage(10)
最后,您可以让每个Game
子类在Visitor
方法中返回其自己类型get_take_damage_visitor
的实例,并且,如果您在每个播放器中存储对游戏的引用,你可以这样做:
Player.do_move_X:
accept(game.get_take_damage_visitor)
优点:
缺点:
Player
和/或Game
的子类型,维护可能会很麻烦 - 尽管这可能适用于任何可能的替代方案,至少在这里编译器可以帮助您。最后,根据语言的不同,如果你有multiple dispatch,你可以更容易地实现类似的想法(实际上,访问者模式是一种技巧,可以实现像没有它的语言中的多个调度一样),或者在带有模板化函数的C ++中。