在 ActionScript 中生成动态 proxy/decorator/encapsulator

Generating a dynamic proxy/decorator/encapsulator in ActionScript

我正在寻找一种方法来做这样的事情:

class ClassA
{
    public function func1():void{}
}

class ClassB extends ClassA
{
    public override function func1():void{
        trace("middleman");
        base.func1();
    }
}

我知道代理 class,但我想要实现的是

var instance:ClassA = new ClassA();
instance = Modifier.messWith(instance); //returns instance with ClassB functionality, without ClassB being defined at compile time

我想要提供额外功能的 class 数量很高,我不想花所有时间为每个功能编写一个扩展 class。另一个用于不同的功能。有什么方法可以实现我正在尝试的目标吗?

编辑: 作为对答案的回应,这是整个事情的最终目标,因为我对这个问题有些混淆:

我需要一种生成动态代理的方法,类似于 Castle.DynamicProxy 在 C# 中所做的,以便创建我正在处理的远程调用概念库。现有的 Proxy class 完全符合我的要求,但有一个致命的缺点 - 它需要代理 class 来扩展 Proxy,这有两个缺点: 1) 我需要代理的所有 classes 都需要扩展代理,这在使用现有代码时会出现问题。 2) 如果一个对象只是简单地包装在一个代理的通用实现中(一个 class 代理一个随机 calasses 的函数调用并用它们做一些事情),该对象不能传递给具有强类型参数的现有函数,因为它没有正确的类型(代理或其扩展是唯一可能的类型)。

我认为你应该分享它的端点目标,因为你要求的东西对于 AS3 概念来说非常陌生。

尽管如此,但有一定的限制,它是可行的。

实施:

package assortie 
{
    public class Wrapper 
    {
        // The public interface to wrap methods.
        static public function wrap(target:Object, name:String, another:Function):void
        {
            var aWrapper:Wrapper = new Wrapper;

            // Remember the wrapped object:method.
            aWrapper.wrapped = target;
            aWrapper.wrappee = target[name];
            aWrapper.wrouter = another;

            // Substitute it with the own method.
            target[name] = aWrapper.delegateWrapper;
        }

        // A stacked list of active wrappers.
        static private var list:Array = new Array;

        // A wrapper method should call Wrapper.recall(...)
        // to invoke the wrapped method.
        static public function recall(...args:Array):*
        {
            var result:*;
            var aWrapper:Wrapper = list[list.length - 1];

            if (aWrapper)
            {
                result = aWrapper.wrappee.apply(aWrapper.wrapped, args);
            }

            return result;
        }

        private var wrapped:Object;
        private var wrappee:Function;
        private var wrouter:Function;

        private function delegateWrapper(...args:Array):*
        {
            // Register self as the last activated wrapper.
            list.push(this);

            var result:*;

            result = wrouter.apply(wrapped, args);

            // Remove self from the active wrappers' list.
            while (list.indexOf(this) > 0)
            {
                list.splice(list.indexOf(this), 1);
            }

            return result;
        }
    }
}

用法:

package assortie 
{
    import assortie.Wrapper;
    import flash.display.Sprite;

    public class MixIn extends Sprite
    {
        public var a:int = 1;

        public function MixIn() 
        {
            // See internal class at the bottom.
            var aMix:MeexEen = new MeexEen;

            // You can stack the wrappers.
            Wrapper.wrap(this, "power", aMix.power);
            Wrapper.wrap(this, "power", aMix.power);
            Wrapper.wrap(this, "power", aMix.power);
            Wrapper.wrap(this, "power", aMix.power);
            Wrapper.wrap(this, "power", aMix.power);

            trace("The Power of Mixing In:", power());
            // Outputs:
            // 2
            // 3
            // 4
            // 5
            // 6
            // The Power of Mixing In: 720
        }

        // The method to be wrapped should be declared as variable.
        // It is not possible to replace a properly declared method.
        public var power:Function = function ():int
        {
            return 1;
        }
    }
}

import assortie.Wrapper;

// Should be either dynamic or contain the declarations
// of all the variables accessed through "this".
internal dynamic class MeexEen
{
    // The wrapping method should be declared as closure.
    // Outherwise the method is bound to its original
    // instance and it is not possible to pass
    // another "this" to the method.
    public var power:Function = function ():int
    {
        this.a += 1;
        trace(this.a);

        return this.a * Wrapper.recall();
    }
}