(请注意:交易卡游戏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;
}
问题:
答案 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)
编辑:虽然这似乎是一个可以从简单重组中受益的继承问题,但我会尊重具有魔术经验的人。