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