【问题标题】:Initializing a lot of fields - constructor vs method return values初始化很多字段 - 构造函数与方法返回值
【发布时间】:2009-07-29 17:28:41
【问题描述】:

我目前正在开发一款 Java 集换式卡牌游戏,类似于旧版 Pokémon 游戏。我现在想做的是以某种方式定义所有卡片,但是因为有很多字段需要初始化,所以我正在考虑其他方法,因为构造函数会很长并且每张卡片都很难读取.我还必须初始化攻击,这意味着我必须基本上每次都创建一个匿名内部类(这个术语正确吗?),如下所示:

/**
 * Base set Abra 43/102
 */
public final class Abra extends Pokemon 
{

    public Abra() 
    {
        super(
                new ImageIcon("img/scans/base-set/43-abra.jpg"), 
                "Abra", 
                "Base Set Abra",
                null, 
                Type.PSYCHIC, 
                Type.PSYCHIC, 
                Type.NONE, 
                30, 
                0
        );

        attack1 = new Attack("Psyshock", Type.NORMAL) 
        {

            /**
             * 10 damage. Flip a coin. If heads, the Defending Pokémon is now Paralyzed.
             */
            public void doAttack() 
            {
                damageApplyWeaknessAndResistance(10);
                if (gui.frames.CoinFlipDialog.showCoinFlipFrame() == CoinFlip.COIN_HEADS) 
                {
                    Game.getOpponentPlayer().getActivePokemon().status = Status.Paralyzed;
                }
            }
        };

        attack2 = null;
    }
}

所以我的第二个选择是使用接口和抽象类创建层次结构,这意味着值不会存储在字段中,而是在需要时由方法返回:

public interface Card extends Cloneable, MouseListener, MouseMotionListener
{
    public String getFullName();

    public ImageIcon getSmallIcon();

    public ImageIcon getFullIcon();

}
public interface Pokemon extends Card 
{
    public String getName();

    public int getHPLeft();

    public int getMaxHP();

    public Type getType();

    public Type getWeakness();

    public Type getResistance();

    public int getRetreatCost();

    public Attack getAttack1();

    public Attack getAttack2();
}

public class Abra extends AbstractPokemon 
{

    @Override
    public Attack getAttack1() 
    {
        return new Abra.PsyShock();
    }

    @Override
    public Attack getAttack2() 
    {
        return null;
    }


    @Override
    public int getMaxHP() 
    {
        return 30;
    }

    @Override
    public String getName() 
    {
        return "Base Set Abra";
    } //etc...

所以我的问题是:这些方法是首选还是有更好的方法?

【问题讨论】:

    标签: java constructor field


    【解决方案1】:

    我建议使用 Builder 模式。点击here查看说明。

    它是 Josh Bloch 推荐的:它是他的《Effective Java 2nd Edition》一书中的第 2 条。

    【讨论】:

    • Builder 模式看起来很有趣,可能适合我的需要。我会试试看。
    • 此外,我建议将数据移动到元数据文件中(可能是 xml 或属性文件)。为每张卡提供一些唯一标识符,并让您的构建者通过查找键入唯一标识符的数据从文件中的数据创建它。如果需要,您甚至可以延迟加载,如果每张卡片都有一张图片并且预加载所有图片很慢,这可能会很有用。
    • 所以现在我一直在尝试使用 Builder 模式,虽然如果我只有静态卡片会很棒,但当我想添加时它并没有真正“感觉正确”——改变像 HP 这样的字段,再加上使用这种方法很难实现攻击(或者我认为如此)。我恢复使用像 Zachery 提议的接口,稍作修改现在应该可以使用。但是,我正在考虑将 XML 与/或某些脚本结合使用。我只想对所有花时间做出回应的人表示感谢。
    • 您也可以通过创建单个方法“攻击”接口来解决它,并在您的 Card 类中创建此接口的实例字段。至于HP领域,当然不一定是最终的。不过,您仍然可以使用构建器对其进行初始化。
    • 我正在使用您所说的攻击接口,尽管实例字段是在 AbstractPokemon 类中定义的。现在的问题是,在每个定义神奇宝贝的类中,我都需要定义攻击的内部类。因为这会导致奇怪的语义错误,例如“return new Abra.PsyShock”以及必须一直创建新对象可能不是一个好方法。您能在这里提出任何改进建议吗?
    【解决方案2】:

    我会采取以下方法:

    有一个类可以充当任何特定卡片的包装器。找到一种方法将每张卡片的数据导出到文件或数据库中,并在程序启动时从文件/数据库中加载卡片。包装器应该能够导入所有卡特定的数据...包装器将具有所有可用的卡处理功能,并且某些功能可能不适用于所有卡。

    另一种方法是使用卡片接口,然后使用该接口设计自定义卡片,为每张卡片创建一个新类。

    根据您希望引擎的可扩展性/灵活性,决定采用的方法。我个人建议使用包装类,并将您的引擎链接到数据库或平面文件。

    【讨论】:

      【解决方案3】:

      我建议使用参数多态性,一张卡的行为取决于它的配置方式。您不仅可以减少类的数量(进而减少复杂性),而且还可以通过将卡片的特征(包装在一个结构中,例如 XML)传递给构造函数来配置卡片。您也可以在“攻击”类上使用这个参数多态性概念。

      【讨论】:

        【解决方案4】:

        必须毫无理由地爱那些-1。我知道这是一个不寻常的观点,但我已经这样做了几十年,有时我不认为典型的方式是最好的方式。如果你真的认为这是错误的,为什么不说为什么?嗯。

        我不是否定你的人,但我对为什么要这样做的看法如下。

        虽然您使用哈希表存储内部数据的想法是可行的,但这并不是原始发布者想要的最佳方法。他的应用(或游戏)涉及很多基于游戏中卡牌属性的操作;它不仅仅是将属性保存到数据库并从数据库中读取它们。在这种情况下,具有正确含义的类型属性将使执行游戏逻辑更容易。请记住,OOD/OOP 是关于适当的封装和具有凝聚力和有意义的结构。

        【讨论】:

          【解决方案5】:

          我肯定会采用数据驱动的方法。每张卡都将共享一组特定的属性。您绝对不想为您的每张卡实现一个新的构建器或 java 类,尤其是在可能有数百张卡的情况下。您将希望您的数据以某种可以读取和解析的格式来创建您的套牌,这样您就可以添加、删除和修改卡片,而无需修改 Java 代码。像 xml 这样的人类可读/可编辑的东西可能会很好用。

          困难在于特殊攻击或其他需要特殊代码处理的项目。在这种情况下,您可以使用像jython 这样的嵌入式脚本引擎,甚至可以使用java 1.6 中的内置javascript 支持。这样您就可以轻松地修改您的卡片库。现在最大的困难是测试你的特殊攻击脚本。

          【讨论】:

            【解决方案6】:

            为什么不把关于单个实体的所有信息放在一个哈希表中并将其包装在一个类中?

            您仍然可以获得封装和所有不错的优势,但您还可以轻松编写代码来将数据绑定到 GUI 或数据库,而无需借助反射。

            您仍然可以在需要与其他代码交互的地方编写 setter 和 getter,但在像您所说的应用程序中,大多数字段都是纯数据,从未被专门操作过(大多数代码访问字段是一般复制和粘贴,并非真正特定于任何领域)。

            您也可以使用单一方法,例如 set("Name", "Abra");或 name=get("姓名");无需编写数十个 setter 和 getter 即可访问哈希中的任何字段...

            在编写了数百个属性类型的屏幕后,我得出了这个结论,其中数据只是从数据库中提取,呈现在屏幕上,修改,然后发送回数据库。这些天来,我的目标是,当我这样做时,我应该能够在没有一行代码的情况下向流程中添加一个新控件——只需修改元数据。

            到屏幕和数据库的绑定甚至验证都可以设置为元数据,然后一切都变得更加容易......

            (更容易假设你和我一样不喜欢复制和粘贴......)

            【讨论】:

            • 毫无理由地爱那些-1。我知道这是一个不寻常的观点,但我已经这样做了几十年,有时我不认为典型的方式是最好的方式。如果你真的认为这是错误的,为什么不说为什么?嗯。
            猜你喜欢
            • 2012-04-11
            • 2013-08-14
            • 2011-02-15
            • 2011-06-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多