【问题标题】:How to choose which child class to instantiate dynamically如何选择动态实例化哪个子类
【发布时间】:2014-05-29 13:42:40
【问题描述】:

我目前的项目是在 as3 中,但这也是我对其他语言感到好奇的地方。

我正在尝试使用工厂对象来动态创建适当的对象。我的LevelFactory 有一个静态方法,它返回提供给该方法的级别编号的新实例。在调用该方法的代码中,我可以动态创建按钮来调用级别,如下所示:

for (var i:int = 1; i < 4; i++) {
            var tempbutton:Sprite = createButton("Level " + i, 25, 25 +(60 * i), start(i));
            _buttons.push(button);
}

这段代码只是用给定的参数(ButtonText, x, y, function) 创建了一个简单的按钮。它工作正常。按钮已创建,单击其中一个按钮将调用此方法并使用适当的参数

private function start(level:int):Function {
        return function(e:MouseEvent):void {
            disableButtons();
            newLevel = LevelFactory.createLevel(level);
            addChild(newLevel);
        }
}

一切正常;我只是为背景上下文提供它。我的问题是:是否可以动态选择我的静态函数返回的对象类型?目前,我正在这样做

public static function createLevel(level:int):Level {
        var result:Level;
        switch(level) {
            case 1: result =  new Level1(); break;
            case 2: result = new Level2(); break;
            //etc
        }
        return result;
}

我应该注意,所有这些 Level1、Level2 等类都扩展了我的基本级别类。 (耶多态性!)我想做的是能够按照以下方式做一些事情

public static function createLevel(level:int):Level {
        var result:Level;
        var levelType:String = "Level" + level;
        return new levelType();
}

显然它不适用于这样的字符串,但是有没有办法在 as3 中实现这一点?那么其他语言呢,例如 Java 或 Python?可以动态选择要实例化什么类型的子类吗?

更新:

import Levels.*;
import flash.events.*;
import flash.utils.*;

public class LevelFactory
{

    public static function createLevel(level:int):Level {
        var ref:Class = getDefinitionByName('Levels.' + 'Level' + level) as Class;
        var result:Level = new ref();
        return result;  
    }
}

更新/编辑:getDefinitionByName 似乎是我正在寻找的,但它有问题。似乎编译器会剥离未使用的导入,这意味着除非我提前在代码中声明每个子类,否则此方法会得到引用错误。如何避免需要分别声明每个类(这违背了动态实例化的目的)?

【问题讨论】:

    标签: actionscript-3 polymorphism


    【解决方案1】:

    是的,你当然可以,而且它与你提供的字符串非常相似。您唯一缺少的是getDefinitionByName 方法:http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()

    你可以生成任何你想要的类名,这个方法的作用是在它的命名空间中搜索那个类,如果找到它 - 它将它作为一个类返回:

    var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
    var instance:Object = new ClassReference();
    

    这段代码将实例化一个 Sprite。这样你就可以在没有所有这些开关和案例的情况下实例化你的类,尤其是当你必须制作一百个关卡时:)

    希望对您有所帮助!干杯!

    编辑:

    在你的情况下,代码应该是:

    var ref:Class = getDefinitionByName('com.path.Level' + index) as Class;
    var level:Level = new ref(); // it will actually be Level1 Class
    

    【讨论】:

    • 不要忘记您需要在代码中的某处导入所有自定义类!否则它们将不会被编译,您将无法在运行时实例化它们。
    • 嗯,这取决于具体情况(图书馆收藏?),但您说的非常对。感谢您的通知! :)
    • 我不太确定我明白你在说什么。我也不能从文档中得到它。让我跑过去看看能不能搞定。我在工厂中使用的代码是:“var ClassReference:Level = getDefinitionByName("Level1") as Level"?
    • getDefinitionByName 将返回 Class 类型的对象。您可以实例化此类,并将其转换为 Level。我会更新示例。
    • 好的,这真的很酷。我还没有完全让它工作,但我想我明白了。即使我已经导入了所有自定义级别类,我仍然收到参考错误。我还尝试了几种不同的途径来获得关卡课程。 com.path.Level 不起作用。 Levels.Level 和 com.path.Levels.Level 也没有。我不断收到 ReferenceError: Error #1065: Variable Level1 is not defined.
    【解决方案2】:

    由于 Andrey 还没有完全帮我完成,所以经过大量研究,我正在为这个问题写一个更完整的答案。

    getDefinitionByName 肯定有我正在寻找的用途。但是,与它在 Java 中的使用不同,您必须对要在代码中某处实例化的类进行硬引用。仅仅导入类是不够的;这样做的原因是编译器将从任何未使用的导入中删除引用以节省空间。因此,如果您导入要动态选择的类包但没有硬引用它们,编译器将取消引用它们。当程序找不到对您的类的适当引用时,这将导致运行时错误。

    请注意,您实际上不必对引用执行任何操作。您只需要声明一个引用,以便可以在运行时找到它。因此,以下代码将消除 switch-case 语句,并允许我在运行时动态声明我正在使用的类。

    {
    import Levels.*;
    import flash.events.*;
    import flash.utils.*;
    /**
     * 
     * Returns the requested level using the createLevel class
     * ...
     * @author Joshua Zollinger
     */
    public class LevelFactory
    {
        Level1, Level2, Level3, Level4, Level5, Level6, Level7;
    
        public static function createLevel(level:int):Level {
            var ref:Class = getDefinitionByName('Levels.Level' + level) as Class;
            var result:Level = new ref(); // it will actually be the correct class
            return result;
        }}}
    

    这样做的明显缺点是您仍然必须对可以像这样实例化的每个类都有一个硬编码的引用。在这种情况下,如果我尝试创建 Level8 实例,它将通过运行时错误,因为未引用 Level8。所以每次我创建一个新的关卡时,我还是得去添加一个对它的引用;我不能只是动态地使用引用。

    据说有一些我尚未测试过的解决方法,例如将类的代码放在单独的 SWF 中并在运行时导入 SWF 或使用具有不同功能的外部库。如果有人有可靠的方法来获得真正动态的引用,而无需在任何地方进行硬编码引用,我很想听听。

    当然,这样还是干净多了;我没有广泛的 switch case 语句来打包所有级别。并且添加对列表的引用比在 switch 中创建新案例更容易和更快。此外,它更接近于动态规划,这通常是一件好事。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 2014-01-26
      相关资源
      最近更新 更多