你如何解决将接口对象强制转换回基类的需求?

时间:2009-05-09 04:37:54

标签: flex actionscript-3 interface

这个问题一般适用于接口,但我会使用AS3 / Flex作为我的语言。如何以不同语言应用它应该是[大部分]显而易见的。

如果我创建了一个基类,并且它扩展了一个接口,则定义了一个显式契约:对于接口中的每个方法,基类必须实现所述方法。

这很容易。但我不明白为什么你有能力将接口实例强制转换回原来的基类。当然,我必须这样做几次(下面的例子非常接近我正在努力的情况),但这并不意味着我理解它:^)

以下是一个示例界面:

public interface IFooable extends IUIComponent {
    function runFoo():void;
}

假设我创建了一个基类,它扩展了VBox并实现了接口:

public class Foo extends VBox implements IFooable {
    public Foo() {
        super();
        //stuff here to create Foo..blah blah
    }
    public function runFoo():void {
        // do something to run foo
    }
}

现在,我使用界面的原因是因为我想保证始终实现“runFoo”。这是我的所有类应该具有的常用功能,无论它们如何实现它。因此,我的父类(应用程序)将通过其接口实例化Foo:

public function init():void {
    var foo:IFooable = new Foo();
    foo.percentHeight = 100; //works because of IUIComponent
}

但是,如果我想将Foo添加到Application容器中,我现在必须将它强制转换回基类(或者不同的基类):

public function init():void {
    var foo:IFooable = new Foo();
    foo.percentHeight = 100;

    addChild(foo as DisplayObject); //_have_ to cast, because addChild takes a 'DisplayObject' class type

    //could also do this:
    //addChild(foo as VBox);
}

原来是不是要隐藏Foo的实现?仍然假设Foo 一个DisplayObject。不幸的是,如果不进行强制转换,就无法将自定义对象添加到容器中。

我完全错过了什么吗?这真的只是Flex / AS3中的一个现象吗?如果你在一个语言的基础API中有一个容器,并且它只允许你添加某个类类型的子类,那么你如何抽象出实现呢?

对于记录,this question似乎会询问此类操作是否可能,但它并没有真正解决为什么它可能是糟糕的设计(以及如何修复它)


第二个想法:

抽象类

正如Matthew所指出的,抽象类有助于解决其中的一些问题:我可以创建一个基础抽象类,它继承自DisplayObject(或者,在我的例子中,VBox,因为它是DisplayObject的子代),并且具有基类实现接口。因此,任何扩展抽象类的类都需要实现其中的方法。

好主意 - 但AS3没有抽象类(据我所知,无论如何)。

所以,我可以创建一个实现接口并扩展VBox的基类,并继承它,我可以在那些需要扩展的方法中插入代码;如果基类是执行程序,这样的代码会抛出错误。不幸的是,这是运行时检查而不是编译时执行。

但它仍然是一个解决方案。


上下文

某些背景可能有所帮助:

我有一个可以有任意数量的子容器的应用程序。这些子容器中的每一个都有各自的配置选项,参数等。但是,应用程序本身具有全局ApplicationControlBar,它将包含用于访问这些配置选项的入口点菜单。因此,每当我将一个子组件添加到主Application(通过“addChild”)时,它也将使用ApplicationControlBar菜单“注册”它自己的配置选项。这保持了与容器本身的可配置性的知识,但允许更加统一的方式来访问它们。

因此,当我创建每个容器时,我想通过它们的接口实例化它们,所以我可以保证它们可以在ApplicationControlBar中注册。但是当我将它们添加到应用程序时,它们需要成为基类。

4 个答案:

答案 0 :(得分:2)

@James Ward,这绝对是我希望的语言,可能是IDisplayObject接口。这将解决AS3中OOP显示编程中的许多问题。

关于原始问题,我过去使用的东西,以及www.as3dp.com上提到的内容是在界面中包含一个getDisplay():DisplayObject方法,通常会返回“this”由其实施者。它不太理想,但有效。

@Matthew Flaschen,虽然我们没有AS3原生的Abstarct类,但通常的做法是将类命名为Abstract,即:AbstarctMyObject,然后将其视为Java和其他语言中的abstarct对象。我们对真正的abstarct类的需求是Flash播放器团队非常清楚的,我们可以在下一版本的ActionScript语言中看到它。

答案 1 :(得分:1)

好吧,我一般都在回答,因为你说,“这真的只是Flex / AS3中的一种现象吗?”。

在你的init方法中,显然你总是用foo调用addChild。这意味着foo必须始终是DisplayObject的一个实例。您还希望它是IFooable的一个实例(虽然这里不清楚为什么)。由于DisplayObject是一个类,因此您可以考虑使用实现IFooable的DisplayObject子类(例如FooableDisplayObject)。在Java中,这将是下面的内容。我不熟悉AS,但我认为这表明接口中没有任何一般缺陷。

interface IFooable
{
    public void runFoo();
}

class DisplayObject
{

}

abstract class FooableDisplayObject extends DisplayObject implements IFooable
{

}

class Foo extends FooableDisplayObject
{
    public void runFoo()
    {

    }
}

public void init()
{
    FooableDisplayObject foo = new Foo();
    foo.percentHeight = 100;

    addChild(foo);
}

答案 2 :(得分:1)

我认为这是Flex / Flash的API不正确的地方。我认为addChild应该采用接口而不是类。但是,由于情况不是这样,你必须施展它。另一种选择是使补丁UIComponent成为一个接口,或者可以添加另一种方法addIChild(IUIComponent)。但那太乱了。所以我建议你file a bug

答案 3 :(得分:0)

这里的情况是,它应该是最佳实践的另一种方式......你不应该将界面转换为显示对象,而是将你的实例作为一个显示对象,然后将其转换为你的界面应用特定方法。

假设我有一个基类Page和其他子类Homepage,Contactpage等。现在你不会将东西应用到基类,因为它是抽象的,但是你设计了子类的接口。

假设子页面实现了一个处理init,addedtostage,loader等等的接口,以及另一个处理逻辑的接口,并最终将base req作为displayobjects进行管理。

设计实现..应该只使用一个接口来处理专门的东西,并从它主要属于的地方扩展子类。现在一个页面有一个'base'意思显示(设计明智......' base'-class是一个displayobject),但可能需要一些专门化,为此构建一个接口来覆盖它。

        public class Page extends Sprite{...}
        public interface IPageLoader{ function loadPage():void{}; function initPage():void{}; }
        public class Homepage extends Page implements IPageLoader
        { function loadPage():void{/*do stuff*/}; function initPage():void{/*do stuff*/}; }

        var currentpage:Page;
        var currentpageLoader:IPageLoader;

        currentpage = new Homepage;
        currentpageLoader = currentpage as IPageLoader;

        currentpageLoader.loadPage();
        currentpageLoader.initPage();

        addChild(currentpage);
        Tween(currentpage, x, CENTER);