AS3 наследование и полиморфизм - PullRequest
1 голос
/ 26 февраля 2012

скажем, я создаю некоторую пользовательскую версию DisplayObject, которая переопределяет некоторые функции, сеттеры и геттеры.Пример:

public class MyDisplayObject extends DisplayObject{
    ....
    override public function get x():Number{
        return 123;
    }
    ....
}

и теперь я хочу сгенерировать класс MyMovieClip, который должен быть таким же, как MovieClip, только с той разницей, что он наследуется от MyDisplayObject вместо DisplayObject.Есть ли простой способ сделать это без необходимости перекодировать всю реализацию MovieClip (что практически невозможно)?

Ответы [ 5 ]

3 голосов
/ 26 февраля 2012

В ActionScript нет фактического множественного наследования.Но если вы можете жить с пониженной производительностью, вы можете расширить класс Proxy для его моделирования.

В вашем случае вы создадите класс, который расширяет Proxy, который содержит экземпляр Object любого типа, и вы передадите тип через конструктор: все вызовы свойств перенаправляются в этот экземпляр, за исключением вызовов на x:

package
{
    import flash.display.MovieClip;
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;


    public dynamic class MyMovieClip extends Proxy
    {
        private var mc:*;

        private function get differentX():Number {
            return 123;
        }

        private function set differentX( x:Number ):void {
            trace( "value received:"+x );
        }

        override flash_proxy function getProperty( name:* ):* {
            if(name == "x") return differentX;
            else return mc[name];
        }

        override flash_proxy function setProperty( name:*, value:* ):void {
            if(name == "x") differentX = value;
            else mc[name] = value;
        }

        public function MyMovieClip (clazz:Class = MovieClip) {
            if (clazz) mc = new clazz();
        }
    }
}

Использование:

var mc : MyMovieClip = new MyMovieClip( Sprite );

mc.x = 1000;  // traces "value received:1000"
trace ("value returned:" + mc.x); // 123;

Очевидно, однако, этот подход ограничен методами, вызываемыми на прокси - вы не можете добавитьПрокси к списку отображения, например.

Если вы действительно хотите в это разобраться, вы также можете воспользоваться библиотекой байт-кода as3commons , которая позволяет вам манипулироватьфактический байт-код, используемый AVM2 во время выполнения.Вы можете использовать его для аспектно-ориентированного программирования путем генерации динамических прокси .

1 голос
/ 26 февраля 2012

Я предлагаю вам использовать сочетание композиции и наследования.Короче говоря:

  1. Создайте свою "дополнительную" функциональность в параллельной серии классов
  2. Создайте свои фактические пользовательские классы DisplayObject, MovieClip и т. Д. Для наследуйте (IS-A) непосредственно из соответствующих встроенных классов Flash, а также из экземпляра HAVE-A ( состав ) соответствующего класса расширения.Чтобы добавить или переопределить элементы в пользовательском классе, вы просто помещаете однострочную оболочку в класс расширения.

Я уверен, что иллюстрация была бы более полезной, так что вот она.Обратите внимание, что мои классы расширений наследуются друг от друга (поэтому мне не нужно переписывать вещи, которые я хочу применить по всей цепочке), но «используемые» классы наследуются от классов Flash.Тогда довольно легко скопировать и вставить несколько строк кода-обертки в каждый используемый класс:

// Base EXTENSION class. Doesn't inherit from anything
package {
    import flash.display.DisplayObject;
    public class DisplayObjectExtensions {
        private var _displayObject:DisplayObject;
        public function DisplayObjectExtensions(displayObject:DisplayObject){
            _displayObject = displayObject;
        }

        // Change the y property to be distance from bottom of stage
        public function get y():Number {
            return _displayObject.stage.stageHeight - _displayObject.y;
        }

        public function set y(value:Number):void {
            _displayObject.y = _displayObject.stage.stageHeight - value;
        }

        // A brand new property 
        private var _customProperty:String;
        public function get customProperty():String { return _customProperty; }
        public function set customProperty(value:String):void { _customProperty = value; }      
    }   
}

// MovieClip EXTENSION class. Inherits from base EXTENSION class:
package  {
    import flash.display.MovieClip;
    public class MovieClipExtensions extends DisplayObjectExtensions {
        private var _movieClip:MovieClip;
        public function MovieClipExtensions(movieClip:MovieClip) {
            super(movieClip);
            _movieClip = movieClip;
        }
        // Adding custom logic to the gotoAndPlay method
        function gotoAndPlay(frame:Object, scene:String = null):void {
            trace("Skipping to frame " + frame + 
                  " in movie clip with customProperty = " + super.customProperty);
            _movieClip.gotoAndPlay(frame, scene);
        }
    }
}

// Useable DisplayObject class: Inherits from flash DisplayObject, has wrappers 
// for extended functionality contained in EXTENSION class.
package  {
    import flash.display.DisplayObject;
    public class MyDisplayObject extends DisplayObject {
        private var _extensions:DisplayObjectExtensions;
        public function MyDisplayObject() { 
            _extensions = new DisplayObjectExtensions(this);
        }
        public override function get y():Number { return _extensions.y; }
        public override function set y(value:Number) { _extensions.y = value; };
        public function get customProperty():String { return _extensions.customProperty; }
        public function set customProperty(value:String):void { _extensions.customProperty = value; }
    }
}

// Useable MovieClip class: Inherits from Flash MovieClip, only needs 
// to duplicate wrapper stubs to "inherit" all the custom extensions.
package  {
    import flash.display.MovieClip;
    public class MyMovieClip extends MovieClip {
        private var _extensions:MovieClipExtensions;
        public function MyMovieClip() { 
            _extensions = new MovieClipExtensions(this);
        }

        // Copy and paste wrappers from MyDisplayObject
        public override function get y():Number { return _extensions.y; }
        public override function set y(value:Number) { _extensions.y = value; };
        public function get customProperty():String { return _extensions.customProperty; }
        public function set customProperty(value:String):void { _extensions.customProperty = value; }

        // Plus the MovieClip-specific override:
        public override function gotoAndPlay(frame:Object, scene:String = null):void {
            _extensions.gotoAndPlay(object, scene);
        }
    }
}
1 голос
/ 26 февраля 2012

Существует метод фальсификации множественного наследования в ActionScript 3, как указано здесь .В основном это включает импорт внешнего файла AS непосредственно в другой класс.Это не красиво и не элегантно, но для всех намерений и целей это потенциальное решение.

Конечно, интерфейс не может расширять другой класс, поэтому вам нужно будет вынести свой пользовательский код из MyDisplayObject, поместить его в отдельныйинтерфейс (возможно, называется MyInterface?), а затем внедрить его в MyDisplayObject (расширение от DisplayObject) и MyMovieClip (расширение из MovieClip), используя технику, описанную на веб-сайте выше.

0 голосов
/ 27 февраля 2012

Видеоклип наследуется от DisplayObject Поэтому все, что вам действительно нужно сделать, это расширить MovieClip, и вы унаследуете все методы из класса DisplayObject.

Я бы также предложил использовать Sprite вместо MovieClip, если вы не используете какие-либо методы кадров.

0 голосов
/ 26 февраля 2012

Ваш пример с DisplayObject слишком сложен, потому что он связан с некоторыми нерегулярными вещами в AS3. DisplayObject - это абстрактный класс, который вы не можете расширить в коде AS3, ближайший, который вы можете получить, это Sprite.

Что касается расширения и, в частности, расширения экранных объектов, мне неприятно звучать как укол, но я все равно спросил бы вас: действительно ли вы этого хотите? На этом пути много неприятностей. Унаследовав от класса, который имеет более 100 методов, вы создаете класс, который потенциально слишком раздут, с поведением, которое вы вряд ли будете полностью контролировать или даже понимать. Это обычная вещь для AS3 (исторически), она дорого обойдется вам с точки зрения разработки и обслуживания. Попробуйте использовать композицию, как было отмечено ранее, или служебные методы, которые специализируются на некоторых аспектах или различиях между вашей версией MovieClip и встроенной.

Однако, в целом, может существовать еще один подход, который очень похож на композицию, но не совсем тот же. Вы можете думать об этом как о «декораторе», но это не совсем то же самое. Идея состоит в том, что у вас есть класс, который не содержит реализации методов, вместо этого он ожидает, что реализации будут предоставлены во время выполнения. Рассмотрим пример ниже:

public class ClassWithDecorator
{

    public function ClassWithDecorator(decorator:Vector.<Function>)
    {
        super();
        this._decorator = decorator;
    }

    public function classMethod(integer:int, string:String):Boolean
    {
        return this._decorator[0].apply(this, [integer, string]);
    }

}

Это позволит вам учитывать разные виды похожих объектов, предоставляя разные наборы методов для каждого класса. Это несколько менее строго с точки зрения типизации - поскольку вы «теряете» тип сигнатуры функции - вы, однако, будете уведомлены во время выполнения, если типы не совпадают. Ну, это компромисс.

Еще один недостаток - вы не сможете «проникнуть» в экземпляры этого класса на месте, где требуются некоторые предопределенные типы, даже если вы реализуете необходимые методы. Тем не менее, в некоторых случаях это жизнеспособные решения, если число различных типов объектов, возможно, слишком велико или не может быть предсказано во время написания кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...