如何从多个类扩展状态

(请注意:交易卡游戏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对象。)

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

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

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

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

考虑使用StateMachine。

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

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

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

您需要重新考虑模型中的IS-A关系。 多重inheritance不可能。 但是您可以使用某些设计模式 – 例如访问者模式或委托模式可能很有用。 效果可能使用命令模式。 让您的基类成员为“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; } 

我根本不知道这个游戏,只是按照你在这里说的:

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

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

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

我在occassion上做的一件事是创建另一个类来实现行为,然后“真正的”类只是将所有内容转发给实用程序类。 所以如果 – 而且我不知道游戏,所以我只是编写例子 – 神器和土地需要一个“购买”function,你可以创建一个神器和土地扩展的属性界面,创建一个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!"); } } 

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