如何选择动态实例化的子类

时间:2014-05-26 04:43:31

标签: actionscript-3 polymorphism

我目前的项目是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等类都扩展了我的基类级别。 (Yay多态性!)我想做的是能够按照

的方式做点什么
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似乎是我正在寻找的,但它有一个问题。似乎编译器将删除未使用的导入,这意味着除非我提前在代码中声明每个子类,否则此方法将获得引用错误。我如何解决单独声明每个类的需要(这违背了动态实例化的目的)?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

由于安德烈没有完全帮助我,所以经过大量研究后,我正在写一个更完整的答案。

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语句来打包所有级别。添加对列表的引用比在交换机中创建新案例更容易,更快捷。此外,它更接近动态编程,这通常是一件好事。