使用继承和多态来解决常见的游戏问题

Bar*_*own 22 java oop inheritance design-patterns

我有两节课; 让我们称他们为食人魔和巫师.(所有字段都是公开的,以便更容易输入示例.)

public class Ogre
{
  int weight;
  int height;
  int axeLength;
}

public class Wizard
{
  int age;
  int IQ;
  int height;
}
Run Code Online (Sandbox Code Playgroud)

在每个课程中,我都可以创建一个名为battle()的方法,该方法将确定如果Ogre遇到Ogre或者向导遇到向导,谁将获胜.这是一个例子.如果食人魔遇到食人魔,那么较重的食人魔会获胜.但是如果重量是相同的,那么具有更长轴的那个获胜.

public Ogre battle(Ogre o)
{
  if (this.height > o.height) return this;
  else if (this.height < o.height) return o;
  else if (this.axeLength > o.axeLength) return this;
  else if (this.axeLength < o.axeLength) return o;
  else return this;    // default case
}
Run Code Online (Sandbox Code Playgroud)

我们可以为奇才制作类似的方法.

但是如果巫师遇到食人魔怎么办?当然,我们可以为此制定一种方法,比较高度.

public Wizard battle(Ogre o)
{
  if (this.height > o.height) return this;
  else if (this.height < o.height) return o;
  else return this;
}
Run Code Online (Sandbox Code Playgroud)

而且我们会为符合巫师的Ogres做一个类似的.但是如果我们必须在程序中添加更多字符类型,事情就会失控.

这是我被卡住的地方.一个显而易见的解决方案是创建具有共同特征的Character类.Ogre和Wizard从Character继承并扩展它以包含定义每个特征的其他特征.

public class Character
{
  int height;

  public Character battle(Character c)
  {
    if (this.height > c.height) return this;
    else if (this.height < c.height) return c;
    else return this;
  }
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来组织课程?我已经看过策略模式和中介模式,但我不确定它们中的任何一个(如果有的话)在这里可以提供什么帮助.我的目标是达到某种常见的战斗方法,这样如果Ogre遇到食人魔它会使用Ogre-vs-Ogre之战,但如果食人魔遇到向导,它会使用更通用的.此外,如果遇到的角色没有共同的特征怎么办?我们如何决定谁赢得了一场战斗?

编辑:很多很棒的回复!我需要消化它们并找出哪种方法最适合我的情况.

Boz*_*zho 19

访问者模式 "是从其所操作的物体结构中分离的算法的方式".

举个例子,你可以拥有

class Character {
    boolean battle(BattleVisitor visitor) {
       return visitor.visit(this);
    }
}

class Ogre extends Character {..}
class Wizard extends Character {..}
class Dwarf extends Character {..}

interface BattleVisitor {
    boolean visit(Ogre character);
    boolean visit(Wizard character);
    boolean visit(Dwarf character);
}

class OgreBattleVisitor implements BattleVisitor {
    private Ogre ogre;
    OgreBattleVisitor(Ogre ogre) { this.ogre = ogre; }
    boolean visit(Ogre ogre) {
      // define the battle 
    }

    boolean visit(Wizard wizard) {
      // define the battle 
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

每当发生战斗时:

targetChar.battle(new OgreBattleVisitor(ogre));
Run Code Online (Sandbox Code Playgroud)

为向导和矮人定义一个访客实现以及出现的任何内容.另请注意,我将visit方法的结果定义为boolean(赢或输),而不是返回获胜者.

因此,在添加新类型时,您必须添加:

  • 访问者处理新类型的方法.
  • 处理新类型战斗的实现

现在,事实证明,如果"Ogre vs Wizard"=="Wizard vs Ogre",你将会有一些重复的代码.我不知道是否是这种情况 - 例如,根据谁先打击,可能会有所不同.此外,你可能想要提供完全不同的算法,比如说"沼泽与食人魔的战斗"与"与食人魔的村庄战斗"相比.因此,您可以创建新访问者(或访问者层次结构)并在需要时应用适当的访问者.

  • @Oscar Reyes这是按要求 - 每个角色应该知道如何因为角色细节而互相争斗.但是你并没有因为与他人打架的方式而与角色捆绑在一起.也就是说,角色(巫师,食人魔)本身并没有与其他人联系在一起.而且,访客模式是GoF,没有我的发明:) (3认同)

Skr*_*rud 7

在两个食人魔具有相同的身高/体重相同的长度的情况下会发生什么?根据你的例子,那个幸运的人可以获得第一名.

我不知道这是否是一个合适的选择,但如果你选择了一个完全不同的方案并将"战斗得分"归因于每个角色而不是依赖于比较个体特征,那该怎么办呢?您可以在公式中使用字符的属性来提供一些可以与另一个字符进行比较的整数.然后,您可以使用通用battle方法比较两个分数,并将字符返回较高的分数.

例如,如果一个食人魔的"战斗得分"是通过他的身高加上他的体重乘以他的斧头长度来计算的,那么巫师的得分是根据他的年龄乘以他的智商来计算的?

abstract class Character {
    public abstract int battleScore();

    public Character battle(Character c1, Character c2) {
        (c1.battleScore() > c2.battleScore()) ? return c1 : c2;
    }
}

class Ogre extends Character {
    public int battleScore() {
        return (height + weight) * axeLength;
    }
 }

 class Wizard extends Character {
    public int battleScore() {
        return height + (age * IQ);
    }
 }
Run Code Online (Sandbox Code Playgroud)

  • 100岁和IQ 200的精灵是如同200岁和IQ 100的精灵一样强大吗?我看到这些只是一些例子,但是存在一个问题,即将想法(年龄,智商,......)扩展为整数.尽管如此,我个人更喜欢这个方向,而不是访客模式. (2认同)

Tro*_*our 5

听起来你想要双重调度.

基本上你OgreWizard(可能)有一个共同的基础.battleOgre基础调用方法时,它接受基础Wizard并调用该基础上的另一个函数Ogre作为参数.两个函数调用的多态行为有效地同时为这两种类型提供了多态性.


Mat*_*ves 4

如何将战斗逻辑分离到它自己的类中,使用类似的方法

Battle(Ogre ogre, Wizard wizard)
Run Code Online (Sandbox Code Playgroud)

这将返回一个包含获胜者的对象(或获胜者本身,无论如何)。这会将战斗逻辑与战斗者分开,并且还允许您通用化,即:

Battle(Creature creat1, Creature creat2)
Run Code Online (Sandbox Code Playgroud)

对于任何没有特定逻辑的生物配对(假设巫师/食人魔/等都以“生物”作为基类)来说,这将是一种后备方法。这将允许您添加/编辑/删除战斗逻辑,而无需修改任何生物本身。

  • 这种方法的问题在于,Java 将查看编译时类型来确定调用哪个版本的“Battle”方法。例如:`Creature c1 = new Ogre(); 生物 c2 = new Wizard(); Battle(c1,c2);`会调用`Battle(Creature,Creature)`而不是**`Battle(Ogre,Wizard)`。 (5认同)