命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

从命令模式的结构图可以看出,它涉及到五个角色,它们分别是:

  • 客户角色:发出一个具体的命令并确定其接受者。
  • 命令角色:声明了一个给所有具体命令类实现的抽象接口
  • 具体命令角色:定义了一个接受者和行为的弱耦合,负责调用接受者的相应方法。
  • 请求者角色:负责调用命令对象执行命令。
  • 接受者角色:负责具体行为的执行。

在下面的情况下可以考虑使用命令模式:

  1. 系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。
  2. 系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。
  3. 如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。
  4. 系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。

C#命令模式:

namespace 命令模式
{
class Program
{
static void Main(string[] args)
{
//开店前的准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter(); //开门营业 顾客点菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1); //点菜完闭,通知厨房
girl.Notify(); Console.Read(); }
} //服务员
public class Waiter
{
private IList<Command> orders = new List<Command>(); //设置订单
public void SetOrder(Command command)
{
if (command.ToString() == "命令模式.BakeChickenWingCommand")
{
Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤。");
}
else
{
orders.Add(command);
Console.WriteLine("增加订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
}
} //取消订单
public void CancelOrder(Command command)
{
orders.Remove(command);
Console.WriteLine("取消订单:" + command.ToString() + " 时间:" + DateTime.Now.ToString());
} //通知全部执行
public void Notify()
{
foreach (Command cmd in orders)
{
cmd.ExcuteCommand();
}
}
} //抽象命令
public abstract class Command
{
protected Barbecuer receiver; public Command(Barbecuer receiver)
{
this.receiver = receiver;
} //执行命令
abstract public void ExcuteCommand();
} //烤羊肉串命令
class BakeMuttonCommand : Command
{
public BakeMuttonCommand(Barbecuer receiver)
: base(receiver)
{ } public override void ExcuteCommand()
{
receiver.BakeMutton();
}
} //烤鸡翅命令
class BakeChickenWingCommand : Command
{
public BakeChickenWingCommand(Barbecuer receiver)
: base(receiver)
{ } public override void ExcuteCommand()
{
receiver.BakeChickenWing();
}
} //烤肉串者
public class Barbecuer
{
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
} public void BakeChickenWing()
{
Console.WriteLine("烤鸡翅!");
}
}
}

Javascript中的命令模式

var bindClick = function(button,func){
button.onclick = func;
}; var MenuBar = {
refresh:function(){
console.log('刷新菜单界面');
}
}; var SubMenu = {
add:function(){
console.log('增加子菜单');
},
del:function(){
console.log('删除子菜单');
}
}; bindClick(button1,MenuBar.refresh);
bindClick(button2,SubMenu.add);
bindClick(button3,SubMenu.del);

命令模式的由来,其实是回调(callback)函数的一个面向对象的替代品。

用闭包实现的命令模式如下代码所示:

var setCommand = function(button,func){
button.onclick = function(){
func();
}
}; var MenuBar = {
refresh:function(){
console.log('刷新菜单界面');
}
}; var RefreshMenuBarCommand = function(receiver){
return function(){
receiver.refresh();
}
}; var refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar); setCommand(button1,refreshMenuBarCommand);

将来有可能还要提供撤销的命令等操作。那我们最好还是把执行函数改为调用execute方法:

var RefreshMenuBarCommand = function(receiver){
return{
execute:function(){
receiver.refresh();
}
}
}; var setCommand = function(button,command){
button.onclick = function(){
command.execute();
}
}; var refreshMenuCommand = RefreshMenuBarCommand(MenuBar);
setCommand(button1,refreshMenuCommand);

撤销命令

var ball = document.getElementById('ball');
var pos = document.getElementById('pos');
var moveBtn = document.getElementById('moveBtn'); var moveCommand = function(receiver,pos){
this.receiver = receiver;
this.pos = pos;
}; moveCommand.prototype.execute = function(){
this.receiver.start('left',this.pos,1000,'strongEaseOut');
}; var moveCommand; moveBtn.onclick = function(){
var animate = new Animate(ball);
moveCommand = new MoveCommand(animate,pos.value);
moveCommand.execute();
};

撤销操作的实现一般是给命令对象增加一个名为unexcude或者undo的方法,在该方法里执行execute的反向操作。

<script>
var ball = doucment.getElementById('ball');
var pos = doucment.getElementById('pos');
var moveBtn = doucment.getElementById('moveBtn');
var cancelBtn = doucment.getElementById('cancelBtn'); var MoveCommand = function(receiver,pos){
this.receicer = receiver;
this.pos = pos;
this.oldPos = null;
}; MoveCommand.prototype.execute = function(){
this.receiver.start('left',this.pos,1000,'strongEaseOut');
this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName];
//记录小球开始移动前的位置
}; MoveCommand.prototype.undo = function(){
this.receiver.start('left',this.oldPos.1000,'strongEaseOut');
//回到小球移动前记录位置
}; var moveCommand; moveBtn.onclick = function(){
var animate = new Animate(ball);
moveCommand = new MoveCommand(animate,pos.value);
moveCommand.execute();
}; cancelBtn.onclick = function(){
moveCommand.undo(); //撤销命令
};
</script>

撤销和重做

比如在一个围棋程序中,现在已经下了10步棋,我们需要一次性悔棋到第5步。在这之前,我们可以把所有执行过的下棋命令都存储在一个历史列表 中,Cnavas画图的程序中,我们却很难为这里的命令对象定义一个擦除某条曲线的undo操作,这时候最好的办法是先清除画布,然后把刚才执行过的命令 全部重新执行一遍,这一点同样可以利用一个历史列表堆栈办到。这是逆转不可逆命令的一个好办法。

<html>
<body>
<button id="replay">播放录像</button>
</body>
<script>
var Ryu = {
attack:function(){
console.log('攻击');
},
defense:function(){
console.log('防御');
},
jump:function(){
console.log('跳跃');
},
crouch:function(){
console.log('蹲下');
}
}; var makeCommand = function(receiver,state){ //创建命令
return function(){
receiver[state]();
}
}; var commands = {
"119":"jump", //W
"115":"crouch", //S
"97":"defense", //A
"100":"attack" //D
}; var commandStack = []; //保存命令的堆栈 document.onkeypress = function(ev){
var keyCode = ev.keyCode,
command = makeCommand(Ryu,commands[keyCode]); if(command){
command(); //执行命令
commandStack.push(command); //将刚刚执行过的命令保存进堆栈
}
}; document.getElementById('replay').onclick = function(){ //点击播放录像
var command;
while(command = commandStack.shift()){ //从堆栈里依次取出命令并执行
command();
}
};
</script>
</html>

命令队列

一个动画结束后该如何通知队列。通常可以使用回调函数来通知队列,除了回调函数之外,还可以选择发布-订阅模式。即在一个动画结束后发布一个消息,订阅者接收到这个消息之后,便开始执行队列里的下一个动画。

宏命令

宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。

var closeDoorCommand = {
execute:function(){
console.log('关门');
}
}; var openPcCommand = {
execute:function(){
console.log('开电脑');
}
}; var openQQCommand = {
execute:function(){
console.log('登录QQ');
}
}; var MacroCmmand = function(){
return{
commandsList:[],
add:function(command){
this.commandsList.push(command);
},
execute:function(){
for(var i=0,command;command = this.commandsList[i++];){
command.execute();
}
}
}
}; var macroCommand = MacroCommand();
macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQCommand); macroCommand.execute();

宏命令是命令模式与组合模式的联用产物。

智能命令与傻瓜命令

一般来说,命令模式都会在command对象中保存一个接收者来负责真正执行客户的请求,这种情况下命令对象是“傻瓜式”的,它只负责把客

户的请求转交给接收者来执行,这种模式的好处是请求发起者和请求接收者之间尽可能地得到了解耦。

但是我们也可以定义一些更“聪明”的命令对象,“聪明”的命令对象可以直接实现请求,这样一来就不再需要接收者的存在,这种“聪明

”的命令对象也叫作智能命令。

js命令模式的更多相关文章

  1. JS命令模式个人理解

    JS命令模式个人理解 //BODY部分<body> <button id="execute">打开电视</button> <button ...

  2. js 命令模式 组合模式

    * 基本宏命令 var closeDoorCommand = { execute: function() { console.log("Closing the door..."); ...

  3. js设计模式-命令模式

    命令模式是一种组织型模式,主要用在把调用对象(用户界面.API和代理等)与实现操作的对象隔离开.也就是说 ,凡是两个对象间的互动方式需要更高的模块化程度时都可以用到这种模式. 命令模式的好处:1.提高 ...

  4. 大熊君说说JS与设计模式之------命令模式Command

    一,总体概要 1,笔者浅谈 日常生活中,我们在看电视的时候,通过遥控器选择我们喜欢的频道时,此时我们就是客户端的角色,遥控器的按钮相当于客户请求,而具体执行的对象就是命令对象, 命令模式把一个请求或者 ...

  5. js设计模式(11)---命令模式

    0.前言 早上好,早晨的时光总是美好的,坐在空调屋里,看着外边的蓝天白云,不停地敲击着键盘,多么美好地享受,也许屌丝就是如此容易满足. 1.什么是命令模式? 用于将一个请求封装为一个对象,从而可用不同 ...

  6. JS 设计模式五 -- 命令模式

    概念 命令模式中的命令(command) 指的是 一个执行某些待定事情的指令. 用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 例子 假设html结构如下: &l ...

  7. js设计模式(六)---命令模式

    命令模式算是最简单.优雅的模式之一了,命令模式中的命令指的是一个执行某些特定事情的指令.目的是吧请求发送者和请求接受者解耦, 就像点餐,顾客只需要发送菜单,谁去接收,不用考虑.厨师接收到命令开始做菜, ...

  8. JS设计模式(6)命令模式

    什么是命令模式? 定义:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化. 主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录. ...

  9. 浅谈js设计模式 — 命令模式

    命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么.此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦 ...

随机推荐

  1. 单片机c语言教程:C51循环语句

    单片机c语言教程第十三课 C51循环语句 循环语句是几乎每个程序都会用到的,它的作用就是用来实现需要反复进行多次的操 作.如一个 12M 的 51 芯片应用电路中要求实现 1 毫秒的延时,那么就要执行 ...

  2. Objective-c 单例模式

    用GCD写Objective-c的单例模式和C#有比较大的区别 声明h文件 #import <Foundation/Foundation.h> @interface me : NSObje ...

  3. MySQL中有关icp mrr和bka的特性

    文辉考我的问题,有关这三个的特性,如果在面试过程中,个人见解可以答以下 icp MyQL数据库会在取出索引的同时,判断是否进行WHERE条件过滤,也就是把WHERE的部分过滤操作放在存储引擎层,在某些 ...

  4. 我的Android进阶之旅------>修改Android签名证书keystore的密码、别名alias以及别名密码

    转载于:http://blog.k-res.net/archives/1229.html  和 http://blog.k-res.net/archives/1671.html ADT允许自定义调试用 ...

  5. Linux中的系统挂载文件/etc/fstab

    [root@localhost ~]# cat /etc/fstab ## /etc/fstab# Created by anaconda on Wed Oct 5 15:21:46 2016## A ...

  6. MySQL学习思维导图

    结束:分享在线下载地址 https://www.xmind.net/m/7t6U/

  7. 1.6 使用电脑测试MC20的读取带中文短信功能

    需要准备的硬件 MC20开发板 1个 https://item.taobao.com/item.htm?id=562661881042 GSM/GPRS天线 1根 https://item.taoba ...

  8. AS(Autonomous System)

    在互联网中,一个自治系统(英文:Autonomous system, AS)是指在一个(有时是多个)实体管辖下的所有IP网络和路由器的 全体,它们对互联网执行共同的路由策略. 自治系统(Autonom ...

  9. JavaScript:学习笔记(1)——在HTML中使用JS

    在HTML中使用JavaScript <script>元素 1.直接在网页中嵌入JS代码 说明: 请不要在代码的任何地方出现</script>字符串 这是由于解析嵌入式代码的规 ...

  10. maven 项目打包时无法解析读取properties文件

    在做项目时遇见一个问题,无法解析properties文件的 内容 异常为 Could not resolve placeholder ......... 在此之前均有做相关的 配置 但是从未出现过如上 ...