设计 - 多个耦合子类的交互

时间:2014-10-13 08:20:37

标签: oop

从简单开始,我有两个对象 - 玩家和游戏:

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类膨胀,以处理每种游戏类型的每个案例。

有没有更好的范例或设计来处理这种情况?

1 个答案:

答案 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)

优点:

  • 您不需要切换或if-elseif构造,也没有垂头丧气。
  • 在编译语言中,如果忘记添加某些案例,则会出现编译错误。
  • 您可以针对类似问题定义其他访问者。

缺点:

  • 虽然这是一种成熟的设计模式,但逻辑起初可能有点令人困惑。
  • 如果您有许多Player和/或Game的子类型,维护可能会很麻烦 - 尽管这可能适用于任何可能的替代方案,至少在这里编译器可以帮助您。

最后,根据语言的不同,如果你有multiple dispatch,你可以更容易地实现类似的想法(实际上,访问者模式是一种技巧,可以实现像没有它的语言中的多个调度一样),或者在带有模板化函数的C ++中。

相关问题