【问题标题】:How to extend states from multiple classes如何从多个类扩展状态
【发布时间】:2011-05-06 16:39:16
【问题描述】:

(请注意:了解集换式卡牌游戏 Magic: The Gathering 将是一个加分项。抱歉,我不知道如何更简单。)

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

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

Permanent 类扩展 Card,并依次由 Creature、Planeswalker、Artifact、Land 和 Enchantment 类扩展。到目前为止,其中只有前两个有自己的字段:

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

问题:

  • 某些永久对象可能会受到影响其子类对象的方法的影响,例如,既作为神器和生物,又作为神器和土地。我知道我可以使用接口来扩展行为,但我不能这样做来扩展状态,除非我使用抽象类,并且在某些情况下,我需要一个具有 Artifacts 和 Creatures 状态的对象。李>
  • 给定子类的大多数永久对象不会受到针对不同子类对象的方法的影响。 (即,仅针对附魔对象的方法不能影响生物对象。)

【问题讨论】:

    标签: java state multiple-inheritance


    【解决方案1】:

    我认为你遇到的问题比你知道的要多。卡片和永久物是有区别的,卡片会产生效果,就像召唤生物一样,而召唤产生的永久物是场上的生物。为了方便起见,游戏中使用卡片来代表这两种事物,但在编程过程中您需要区分它们。

    MTG 存在永久物与卡牌没有直接关系的情况(像蜂巢这样的卡牌生成仅由衍生物代表的生物,它们没有卡牌代表),因此您的继承层次不适用于此。虽然你需要在一张牌和它所召唤的生物之间保持联系(这样你就知道什么时候把牌放回弃牌堆了),我不认为你列出的具体继承关系会是有用。您可能需要一种用于卡片的层次结构,而另一种用于永久物的层次结构。不要忽视层次结构的可能性(因为可以有效利用许多继承关系),只需注意将不同的事物分开。

    我建议阅读 Steve Yegge 在 the Universal Design Pattern 上的博文,其中他谈到了在他的游戏中使用属性模式(这似乎与 MTG 一样灵活)。显然,游戏中事物的许多特征都是可变的(包括什么是生物,什么不是,因为有些牌可以将地变为生物),并且处理属性和对其进行更改(包括到期的临时更改)的系统是将是必不可少的。就其本身而言,类层次结构不够灵活,无法适应 MTG 中的各种效果。

    MTG 的有趣之处在于,您拥有这个表面上的主题领域(由诸如生物、法术、神器、土地等之类的事物组成),看起来可以理解且可靠,但规则允许这些事物以不可预知的方式发生变化(特别是因为总是可以引入新卡)。主题域是通过游戏域(操纵主题域的规则集)实现的。如果您将主题领域硬编码到您的实现中(通过扩展超类或实现接口来表示土地或人工制品),总会遇到您无法做的事情,并且解决这些问题可能很难或不可能。阅读规则,注意引入的技术术语(卡牌、永久物、衍生物、效果等),因为这是您需要实施的真正领域。

    【讨论】:

    • 你对令牌案例是正确的,我同意。我想一旦我弄清楚了这个问题,我就可以找到一种方法来解决这个问题,但似乎情况并非如此。我会给 Steve Yegge 的帖子读一读。非常感谢您的提示:)
    【解决方案2】:

    考虑为此使用 StateMachine。

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

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

    【讨论】:

    • 目前实施您的建议超出了我的专业知识,但至少我知道要瞄准可以解决问题的东西。谢谢:)
    【解决方案3】:

    当你开始与神器生物或神器地等打交道时,你会遇到更严重的问题。

    您需要重新考虑模型中的 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;
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

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

      我偶尔做的一件事是创建另一个类来实现该行为,然后“真实”类将所有内容转发给实用程序类。因此,如果 - 并且我不了解游戏,所以我只是在编造示例 - Artifact 和 Land 都需要“购买”功能,您可以创建一个 Artifact 和 Land 都扩展的 Property 接口,创建一个包含实际代码的 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!");
        }
      }
      

      【讨论】:

        【解决方案5】:

        编辑:虽然这似乎是一个继承问题,可以从简单的重组中受益,但我会听从那些有万智牌经验的人。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-04-19
          相关资源
          最近更新 更多