js命令模式
命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
从命令模式的结构图可以看出,它涉及到五个角色,它们分别是:
- 客户角色:发出一个具体的命令并确定其接受者。
- 命令角色:声明了一个给所有具体命令类实现的抽象接口
- 具体命令角色:定义了一个接受者和行为的弱耦合,负责调用接受者的相应方法。
- 请求者角色:负责调用命令对象执行命令。
- 接受者角色:负责具体行为的执行。
在下面的情况下可以考虑使用命令模式:
- 系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。
- 系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。
- 如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。
- 系统需要使用命令模式作为“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命令模式的更多相关文章
- JS命令模式个人理解
JS命令模式个人理解 //BODY部分<body> <button id="execute">打开电视</button> <button ...
- js 命令模式 组合模式
* 基本宏命令 var closeDoorCommand = { execute: function() { console.log("Closing the door..."); ...
- js设计模式-命令模式
命令模式是一种组织型模式,主要用在把调用对象(用户界面.API和代理等)与实现操作的对象隔离开.也就是说 ,凡是两个对象间的互动方式需要更高的模块化程度时都可以用到这种模式. 命令模式的好处:1.提高 ...
- 大熊君说说JS与设计模式之------命令模式Command
一,总体概要 1,笔者浅谈 日常生活中,我们在看电视的时候,通过遥控器选择我们喜欢的频道时,此时我们就是客户端的角色,遥控器的按钮相当于客户请求,而具体执行的对象就是命令对象, 命令模式把一个请求或者 ...
- js设计模式(11)---命令模式
0.前言 早上好,早晨的时光总是美好的,坐在空调屋里,看着外边的蓝天白云,不停地敲击着键盘,多么美好地享受,也许屌丝就是如此容易满足. 1.什么是命令模式? 用于将一个请求封装为一个对象,从而可用不同 ...
- JS 设计模式五 -- 命令模式
概念 命令模式中的命令(command) 指的是 一个执行某些待定事情的指令. 用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 例子 假设html结构如下: &l ...
- js设计模式(六)---命令模式
命令模式算是最简单.优雅的模式之一了,命令模式中的命令指的是一个执行某些特定事情的指令.目的是吧请求发送者和请求接受者解耦, 就像点餐,顾客只需要发送菜单,谁去接收,不用考虑.厨师接收到命令开始做菜, ...
- JS设计模式(6)命令模式
什么是命令模式? 定义:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化. 主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录. ...
- 浅谈js设计模式 — 命令模式
命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么.此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦 ...
随机推荐
- websocket集群情况下Nginx 代理出现的坑
那么问题的背景: A想给B发送socket 消息 ! A这消息 这时候被Nginx 轮询发到了C 服务器上! 擦! 这时候就蛋疼了! 要接收消息那个人在B服务器上! B就这样苦逼的收不 ...
- spring多数据源事务配置
项目中遇到多数据源问题, 对于每个数据源需要单独完成事务控制, 这里记录下具体实现方法 在spring配置文件中 定义两个数据源 <!-- 数据源定义(spring-jndi) --> ...
- PL/SQL 入门
1. 概述 PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,指在 SQL 命令语言中增加了 过程处理语句(如分支,循环等),使 SQL 语言 ...
- 2014-08-28——PC端几款主流浏览器的内核
Trident(IE浏览器) Mozilla(Gecko)(熟悉的有Firefox,Flock等浏览器) WebKit(熟悉的有Safari.Chrome等浏览器) Opera(presto)(Ope ...
- Apache JServ Protocol (AJP)
The Apache JServ Protocol (AJP) is a binary protocol that can proxy inbound requests from a web serv ...
- Axure快捷键
基本快捷键: 打开:Ctrl + O 新建:Ctrl + N 保存:Ctrl + S 退出:Alt + F4 打印:Ctrl + P 查找:Ctrl + F 替换:Ctrl + H 复制:Ctrl + ...
- 转载:阮一峰 理解RESTful架构
转载 http://www.ruanyifeng.com/blog/2011/09/restful.html 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件&q ...
- jQuery EasyUI - 数据表格(DataGrid)
由于工作需要,项目使用前端 jQuery EasyUI - DataGrid 来控制数据表格. 1.加载相关js和css,因为easyui依赖jquery,所有加载easyui前要先加载jquery, ...
- position:absolute width
position:absolute; left:0px; right:0px; top:0px; bottom:0px; 设置布满整个父范围,设置了absolute的元素可以通过以上4个属性来展开面, ...
- Java字段初始化规律:
Java字段初始化规律: Java进行初始化的地方有两个,初始化块和构造函数,其中初始化块又分为静态初始化块和实例初始化块(以上程序为实例初始化块).静态初始化块是类中由static修饰的初始化块,实 ...