如何从多个类扩展状态

时间:2011-05-06 16:39:17

标签: java state multiple-inheritance

(请注意:交易卡游戏Magic:The Gathering的知识将在这里加分。抱歉,我不知道如何更轻松。)

我使用 Java 遇到了一个问题,我将描述如下......我有一个名为Card的基本类,具有以下所有属性:

public class Card{
    String Name;
    String RulesText;
    String FlavorText;
    String Cost;
    int ConvertedCost;
    String Rarity;
    int Number;
}

永久类扩展卡,并由生物,飞行者,神器,土地和结界等类扩展。到目前为止,只有前两个拥有自己的领域:

public class Creature extends Permanent{
    int Power;
    int Toughness;
}
public class Planeswalker extends Permanent{
    int Loyalty;
}

问题:

  • 某些永久对象可能受影响其子类对象的方法的影响,例如,作为工件和生物,或作为工件和土地。我知道我可以使用接口来扩展行为,但除非我使用抽象类,否则我不能这样做以扩展状态,在某些情况下我需要一个状态为 Artifacts和Creatures的对象。
  • 通过定位不同子类的对象的方法,给定子类的大多数永久对象不会受到影响。 (即,一种仅以Enchantment对象为目标的方法不会影响Creature对象。)

5 个答案:

答案 0 :(得分:4)

我认为你遇到的问题比你知道的多。一张牌和一张永久牌之间存在差异,一张牌发挥作用,如召唤一个生物,而召唤所产生的永久物就是游戏中的生物。卡片在游戏中用于表示方便,但在编程期间,您需要区分它们。

MTG的情况下,永久物与卡片没有直接关系(像Hive这样的卡片生成的生物仅由代币代表,但它们没有卡片代表),因此您的继承层次结构不起作用。虽然你需要保持卡与被召唤的生物之间的联系(所以你知道什么时候把卡送回丢弃堆),我不认为你所设定的特定继承关系将会是有用。您可能需要一个卡层次结构和另一个不同的永久层次结构。不要忽略层次结构的可能性(因为有许多可以有效利用的继承关系),只需要小心将单独的事物分开。

我建议在the Universal Design Pattern上阅读Steve Yegge的博文,在那里他谈到了为他的游戏使用一个属性模式(这看起来和MTG一样灵活)。显然,游戏中的许多东西的特征是可变的(包括什么是生物,什么不是,因为有卡片可以改变生物的土地),并且处理属性和改变它们的系统(包括到期的临时变化)是将是至关重要的。一个类层次结构本身不足以适应MTG中的各种效果。

让MTG变得有趣的是你有这个表面主题领域(由生物,法术,文物,土地等组成的东西),这些领域似乎可以理解且可靠,但规则允许这些东西以不可预测的方式改变(特别是因为总是可以引入新卡)。主题域通过游戏域(操纵主题域的规则集)来实现。如果您将主题域硬编码到您的实现中(通过扩展超类或实现接口来表示Lands或Artifacts),总会遇到您不能做的事情,并且解决这些问题可能很难或不可能。阅读规则,注意引入的技术术语(卡片,永久物,令牌,效果等),因为这是您需要实施的真实域名。

答案 1 :(得分:2)

考虑使用StateMachine。

你有一个CreatureStateMachine和一个ArtifactStateMachine。实现这两个接口的对象可以传递给StateMachine。每个StateMachine都负责管理不同属性集的值。

对象本身会生成事件并将它们传递给StateMachines,StateMachines会忽略或相应地处理它们。 StateMachine更新被管理对象的状态,而Object本身只知道它所处的状态。

答案 2 :(得分:1)

当你开始处理神器生物或神器之地等时,你会遇到更糟糕的问题。

您需要重新考虑模型中的IS-A关系。多重继承不可能。但是您可以使用某些设计模式 - 例如访问者模式或委托模式可能很有用。效果可能使用命令模式。让您的基类成员为“TypeTags”甚至是有用的 - 所有有效定位规则的列表。

例如,您可以创建一个生物(伪代码):

Creature thing = new Creature("My Name", toughness, power, {artifact,creature,enchantment});

每个“效果”操作都会接受一个Card对象:

Card doEffect(InCard, validTargets) {
  if (validTargets.Intersection(Incard.targets).length > 0) //we have valid targets
    return actuallyDoEffect(InCard);
  else
    return InCard;
}

答案 3 :(得分:1)

我完全不知道这个游戏,只是按照你在这里说的那样:

多级层次结构可能会解决您的问题。就像Artifacts和Lands都有一些共同的行为一样,那么不是说Artifact延伸永久而Land延伸永久,你可以创建另一个类,让我们称之为Property,然后说Property延伸永久,Artifact扩展Property,Land扩展Property。 (我正在考虑“不动产”和“个人财产”意义上的“财产”,而不是对象的财产。无论如何。)然后,Artifact和Land共有的任何功能或数据都可以在财产中。

但是如果Artifact和Land中存在不适用于Creature的行为,以及Artifact和Creature中不适用于Land的其他行为,则此方法将无效。

Java类不能扩展多个其他类,但当然它可以实现任意数量的类。因此,您可以为任何给定的对象集创建通用行为的接口,例如可能由Artifact和Creature实现的Movable接口,以及将由Artifact和Land等实现的Property接口。但是因为我是确定你意识到,这只是继承了声明,而不是代码。因此,您最终必须多次实现相同的代码。

我在occassion上做的一件事是创建另一个类来实现行为,然后“真正的”类只是将所有内容转发给实用程序类。所以如果 - 而且我不知道游戏,所以我只是编写例子 - 神器和土地需要一个“购买”功能,你可以创建一个神器和土地扩展的属性界面,创建一个PropertyImplementation类包含实际代码,然后你会有类似的东西:

public class PropertyImplementation
{
  public static void buy(Property property, Buyer buyer, int gold)
  {
    buyer.purse-=gold;
    property.owner=buyer;
    ... etc, whatever ...
  }
}
public interface Property
{
  public static void buy(Property property, Buyer buyer, int gold);
}
public class Land extends Permanent implements Property
{
  public void buy(Buyer buyer, int gold)
  {
    PropertyImplementation.buy(this, buyer, gold);
  }
  ... other stuff ...
}
public class Artifact extends Permanent implements Property
{
  public void buy(Buyer buyer, int gold)
  {
    PropertyImplementation.buy(this, buyer, gold);
  }
  ... other stuff ...
}

不太复杂但有时非常实用的方法是在不适用的调用时抛出错误。像:

public class Permanent
{
  public void buy(Buyer buyer, int gold)
  throws InapplicableException
  {
    buyer.purse-=gold;
    this.owner=buyer;
    ... etc ...
  }
}
public class Plainsman extends Permanent
{
  // override
  public void buy(Buyer buy, int gold)
  throws InapplicableException
  {
    throw new InapplicableException("You can't buy a Plainsman! That would be slavery!");
  }
}

答案 4 :(得分:0)

编辑:虽然这似乎是一个可以从简单重组中受益的继承问题,但我会尊重具有魔术经验的人。