命令模式(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. 巨蟒python全栈开发flask1

    1.整体把握 (1)路飞学城 - RestAPI 前后端分离开发 Django Vue.js - DRF DjangoRestFromwork - 线上商城的经验 (2)智能玩具 - RestAPI ...

  2. Python星号*与**用法分析 What does ** (double star/asterisk) and * (star/asterisk) do for parameters? 必选参数 默认参数 可变参数 关键字参数

    python中*号**的区别 - CSDN博客 https://blog.csdn.net/qq_26815677/article/details/78091452 定义可变参数和定义 list 或 ...

  3. python系列六:Python3元组tuple

    '''元组与列表类似,不同之处在于元组的元素不能修改.元组使用小括号,列表使用方括号.''''''uple元素不可变有一种特殊情况,当元素是可变对象时.对象内部属性是可以修改的!tuple的不可变限制 ...

  4. Linux学习拾遗

    一.安装iso文件 首先建立一个目录作为挂载点:# mkdir /mnt/iso 获得root权限然后使用下面的参数挂载ISO映像文件:# mount -t iso9660 /path/image.i ...

  5. Bean\Entity\Model\POJO\Dto\EJB简单解析

    一.Bean 对于Bean而言,只要是Java的类的就可以称为一个Bean, 更用在Spring上,被Spring管理的对象就可以将其称作为Bean. 它不仅仅可以包括对象的属性以及get,set方法 ...

  6. 如何定义 match 常量?

    namespace MathConstants { const double E = 2.71828182845904523536; // e const double LOG2E = 1.44269 ...

  7. SQL判断字符类型是否为数字

    用ISNUMERIC函数 确定表达式是否为一个有效的数字类型. 语法 ISNUMERIC ( expression ) 参数 expression 要计算的表达式. 返回类型 int 注释 当输入表达 ...

  8. Python获取主机名

    import socket print socket.gethostname()

  9. (4.11)sql server内存使用

    一些内存使用错误理解   开篇小感悟 在实际的场景中会遇到各种奇怪的问题,为什么会感觉到奇怪,因为没有理论支撑的东西才感觉到奇怪,SQL Server自己管理内存,我们可以干预的方式也很少,所以日常很 ...

  10. unknown facet type would you like to ignore facet from module

    去idea plugin 里面把红色的插件 重新勾选一下,点apply 重启就可以了