Actionscript 3 命令模式和AS3

Actionscript 3 命令模式和AS3,actionscript-3,flash,design-patterns,Actionscript 3,Flash,Design Patterns,只是好奇你们是如何解决命令封装的问题的。是否为每个命令创建单独的类?或者有另一种方法(没有大量的课程) 注意,我在动作脚本3中遇到了问题 Upd:更准确地说,我想知道如何组织与命令相关的机器(例如,每个命令的类) 提前谢谢你 命令模式掩盖了Java缺少高阶函数(无法传递对函数的引用)。在AS/其他ES语言中使用此模式没有任何意义,因为在那里不存在此问题 非常不幸的是,学术界现在使用Java进行CS研究,特别是如果CS不是你的专业。这可以解释在没有任何批判性分析的情况下将这个和其他Java ISM

只是好奇你们是如何解决命令封装的问题的。是否为每个命令创建单独的类?或者有另一种方法(没有大量的课程)

注意,我在动作脚本3中遇到了问题

Upd:更准确地说,我想知道如何组织与命令相关的机器(例如,每个命令的类)


提前谢谢你

命令模式掩盖了Java缺少高阶函数(无法传递对函数的引用)。在AS/其他ES语言中使用此模式没有任何意义,因为在那里不存在此问题


非常不幸的是,学术界现在使用Java进行CS研究,特别是如果CS不是你的专业。这可以解释在没有任何批判性分析的情况下将这个和其他Java ISM移植到其他语言中的原因。

我对每个命令使用一个类。对于模式为某些问题域提供的灵活性来说,多个类是一个很小的代价

使用一个游戏示例,使用一个名为GameEntity的自定义类—player、敌军等—我首先定义一个命令接口

ICommand{
    function execute():void;
}
然后,每个命令类在注入构造函数的接收器上实现execute方法:

public class GoRight implements ICommand{

    _gameEntity:GameEntity

    public function GoRight(entity:GameEntity){
        _gameEntity = entity;
    }

    public function execute():void{
        _gameEntity.x++;
    }
}

每一个命令都是如此

如果使用一个堆栈,其中一个命令在另一个命令完成后被调用,则需要添加eventlisteners来侦听命令完成,作为启动下一个命令的触发器


然而,如果你发现自己正在构建一个软件,其中命令模式成为一个关键的架构解决方案,我会仔细考虑actionscript或者Flash player是否真的是正确的工具。对于任务队列和长命令链的执行/撤消,并发性(Flash不提供的东西)对于良好的用户体验变得非常重要。当然,这只是我的观点…

命令模式是关于在三种不同类型的对象之间分离关注点:

  • 调用者
  • 接受者
  • 命令
  • 正如wvxvw所指出的,这通常意味着您有大量实现
    ICommand
    的类,它们实际上只是作为在接收器上调用方法的代理——这是Java所必需的。随着时间的推移,这可能会变得有点难以管理

    但让我们看看一些代码,我将遵循示例代码,该代码介绍了所发现的命令模式的基本知识,但为清晰起见稍微简化了一些:

    因此,首先,接收者:

    以及调用方:

    // invoker
    public class CaptPicard {
        private var _command:ICommand;
    
        public function CaptPicard() { }
    
        public function set command(cmd:ICommand):void {
            this._command = cmd;
        }
    
        public function issueCommand():void {
    
        }
    }
    
    最后是一些命令:

    // command
    public interface ICommand {
        function execute():void;
    }
    
    public class EngageCommand implements ICommand 
    {
        private var receiver:IStarShip
    
        public function EngageCommand(receiver:IStarShip) {
            this.receiver = receiver;
        }
    
        public function execute():void {
            receiver.engage();
        }
    }
    
    public class SelfDestructCommand implements ICommand 
    {
        private var receiver:IStarShip
    
        public function SelfDestructCommand(receiver:IStarShip) {
            this.receiver = receiver;
        }
    
        public function execute():void {
            receiver.selfDestruct();
        }
    }   
    
    public class MakeItSoCommand implements ICommand 
    {
        private var receiver:IStarShip
    
        public function MakeItSoCommand(receiver:IStarShip) {
            this.receiver = receiver;
        }
    
        public function execute():void {
            receiver.makeItSo();
        }
    }   
    
    我们可以这样说:

    var enterprise:Enterprise = new Enterprise;
    var picard:CaptPicard = new CaptPicard();
    picard.command = new SelfDestructCommand(enterprise);
    picard.issueCommand();
    
    var enterpriseA:Enterprise = new Enterprise();
    var enterpriseB:Enterprise = new Enterprise();
    var picard:CaptPicard = new CaptPicard();
    
    var selfDestuctor:StarShipCommandFactory = new StarShipCommandFactory(function(starShip:IStarShip):void {
        starShip.selfDestruct();
    } );
    
    var blowUpA:ICommand = selfDestructor.to(enterpriseA);
    var blowUpB:ICommand = selfDestructor.to(enterpriseB);
    
    picard.command = blowUpA;
    picard.issueCommand();
    
    picard.command = blowUpB;
    picard.issueCommand();
    
    到目前为止,它与示例代码非常接近,是ActionScript中命令模式的标准实现。但是,现在我们有三个
    ICommand
    实现,随着接收者可以做的事情数量的增加,编排模式所需的命令数量也在增加

    如果我们开始检查命令本身,它除了告诉接收者做一些工作之外,实际上没有什么作用。正如wvxvw所暗示的,actionscript中已经有了这样的功能:第一类函数

    让我们看看可能的实现,以减少ICommand实现的数量,您需要在不改变模式的情况下四处浮动

    假设我们制作了一个通用类型的命令,假设:

    public class GenericCommand implements ICommand {
    
        private var worker:Function;
    
        public function GenericCommand(worker:Function) {
            this.worker = worker;
        }
    
        public function execute():void {
            this.worker.call();
        }
    }
    
    请注意,我们的新类型仍然实现了
    ICommand
    ,但它不是直接绑定到某个实现,而是接受一个worker来执行某些工作。它不会立即执行它,它只是抓住它,等待其他东西使它启动

    然后我们可以将代码转换为如下内容:

    var enterprise:Enterprise = new Enterprise;
    var picard:CaptPicard = new CaptPicard();
    picard.command = new GenericCommand(function() { enterprise.selfDestruct(); });
    picard.issueCommand();
    picard.command = new GenericCommand(function() { enterprise.engage(); });
    picard.issueCommand();
    picard.command = new GenericCommand(function() { enterprise.makeItSo(); });
    picard.issueCommand();
    
    使用这个
    GenericCommand
    ,我们可以将所有命令展开到这个新模式中(或者在它们之间进行混合和匹配)

    但是如果你仔细观察,你会发现通过使用它,我们通过引用封闭变量
    enterprise
    ,将通用命令与IStarShip紧密耦合,也许更令人担忧的是,如果我们不小心,我们可能会创建大量这样的闭包。它们都需要在某个时候进行垃圾收集,这可能会影响性能,甚至导致内存泄漏

    我们可以将这一层进一步解耦,使其更加动态。我们可以使用工厂模式来帮助动态生成命令,而不是直接使用局部变量
    enterprise
    。考虑这一点:

    public class StarShipCommandFactory {
        private var worker:Function;
    
        public function StarShipCommandFactory(worker:Function) {
            this.worker = worker;
        }
    
        public function to(receiver:IStarShip):ICommand {
            return new GenericCommand(function() {
                worker.call(undefined, receiver);
            });
        }
    }
    
    然后我们可以使用它来创建一个命令工厂来创建这些命令。比如:

    var enterprise:Enterprise = new Enterprise;
    var picard:CaptPicard = new CaptPicard();
    picard.command = new SelfDestructCommand(enterprise);
    picard.issueCommand();
    
    var enterpriseA:Enterprise = new Enterprise();
    var enterpriseB:Enterprise = new Enterprise();
    var picard:CaptPicard = new CaptPicard();
    
    var selfDestuctor:StarShipCommandFactory = new StarShipCommandFactory(function(starShip:IStarShip):void {
        starShip.selfDestruct();
    } );
    
    var blowUpA:ICommand = selfDestructor.to(enterpriseA);
    var blowUpB:ICommand = selfDestructor.to(enterpriseB);
    
    picard.command = blowUpA;
    picard.issueCommand();
    
    picard.command = blowUpB;
    picard.issueCommand();
    
    这将减少您需要生成的静态类的数量,以利于动态创建的对象利用第一类函数的属性,但仍然应用相同的总体思想

    事实上,使用这种模式,您可以构建非常复杂的命令,代理多个接收器上的多个操作,这可能是一件非常强大的事情

    那么,为什么要使用
    ICommand
    的传统实现呢? 坚持传统模式的最大原因之一是易于序列化。因为对象是显式的,所以很容易序列化。假设你有一堆向量。。您可以轻松地序列化它们、写出它们,然后重新加载它们并按顺序重放它们,并且应该与您的状态完全相同。动态生成的对象,比如我前面描述的对象,要做到这一点需要一些技巧——不是不可能,只是更复杂

    如果您关心维护大量映射到接收器操作的
    ICommand
    s,那么在过去我使用元编程来解决这个问题。使用ruby/python解析一组文件,并自动生成
    ICommand