博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过两个例子来理解js中的命令模式
阅读量:6120 次
发布时间:2019-06-21

本文共 8271 字,大约阅读时间需要 27 分钟。

hot3.png

接口模式中有三个参与者(三个对象):客户-client,调用者-invoking 和接收者-receiving。客户对象就是页面上的控件(能绑定鼠标键盘等事件的dom元素),接收用户的操作,接收者对象提供实现具体的功能的方法,命令对象与客户对象绑定,用来实现客户对象与接收者之间的低耦合,也就是弱化按钮之类的用户界面元素与其功能之间的耦合。客户对象与命令对象直接联系,它们之间的绑定必须通过接口来规范。

首先是命令模式中必须用到的接口类检查工具

/*接口定义类,命令模式必须依赖接口*//*第一个参数是接口名称,第二个参数是接口中预定义的方法*/var Interface = function(name, methods){	if(arguments.length != 2){		throw new Error('Interface constructor called with "'+ arguments.length +'" arguments, but expected exactly 2.');	}	this.name = name;	this.methods = [];	for(var i=0,len=methods.length; i

第一个例子:

我们制作一个具有桌面应用程序风格的菜单栏,菜单栏中有多个菜单项,并通过使用命令对象,让这些菜单项执行各种操作。

/*接收者类,实现具体的操作*/var FileActions = {	open: function(){alert('FileActions.open')},	close: function(){alert('FileActions.close')},	save: function(){alert('FileActions.save')},	saveAs: function(){alert('FileActions.saveAs')}};var EditActions = {	cut: function(){alert('EditActions.cut')},	copy: function(){alert('EditActions.copy')},	paste: function(){alert('EditActions.paste')},	del: function(){alert('EditActions.del')}};var InsertActions = {	textBlock: function(){alert('InsertActions.textBlock')},	image: function(){alert('InsertActions.image')}};var HelpActions = {	showHelp: function(){alert('HelpActions.showHelp')}};/*接口定义*/var Command = new Interface('Command', ['execute']);var Composite = new Interface('Composite', ['add', 'remove', 'getChild', 'getElement']);var MenuObject = new Interface('MenuObject', ['show']);/*菜单组合对象*/var MenuBar = function(){	this.menus = {};	this.element = document.createElement('ul');	this.element.style.display = 'none';};MenuBar.prototype = {/*接口方法的实现*/	add: function(menuObject){		Interface.ensureImplements(menuObject, Composite, MenuObject);		this.menus[menuObject.name] = menuObject;		this.element.appendChild(menuObject.getElement());	},	remove: function(name){		delete this.menus[name];	},	getChild: function(name){		return this.menus[name];	},	getElement: function(){		return this.element;	},	show: function(){		this.element.style.display = 'block';		for(var name in this.menus){			this.menus[name].show();		}	}};var Menu = function(name){	this.name = name;	this.items = {};	this.element = document.createElement('li');	this.element.innerHTML = this.name;	this.element.style.display = 'none';	this.container = document.createElement('ul');	this.element.appendChild(this.container);/*在添加到页面之前,添加的子节点是动态改变的*/};Menu.prototype = {/*接口方法的实现*/	add: function(itemObject){		Interface.ensureImplements(itemObject, Composite, MenuObject);		this.items[itemObject.name] = itemObject;		//this.element.querySelector('ul').appendChild(itemObject.getElement());		this.container.appendChild(itemObject.getElement());	},	remove: function(name){		delete this.items[name];	},	getChild: function(name){		return this.items[name];	},	getElement: function(){		return this.element;	},	show: function(){		this.element.style.display = 'block';		for(var name in this.items){			this.items[name].show();		}	}};/*作为调用者类	*命令模式的作用在此开始显现出来。你可以创建一个包含有许多菜单的非常复杂的菜单栏,而每个菜单栏都包含着一些菜单项(MenuItem),每个MenuItem都与一个命令对象绑定在一起。这些菜单项对如何执行自己所绑定的操作一无所知,它们也不需要知道那些细节,它们唯一需要知道的就是命令对象都有一个execute方法。	*/	/*第一个参数为菜单名称,第二个参数为要绑定的命令对象*/var MenuItem = function(name, command){	Interface.ensureImplements(command, Command);	this.name = name;	this.element = document.createElement('li');	this.element.style.display = 'none';	this.anchor = document.createElement('a');	this.anchor.innerHTML = this.name;	this.anchor.href = '#';	this.element.appendChild(this.anchor);		/*在事件回调中调把命令对象与调用者绑定*/	this.anchor.addEventListener('click', function(e){		e.preventDefault();		command.execute();/*就这么简单的一句*/	});};MenuItem.prototype = {	/*这些方法因为在接口中有定义,必须列出来,但在这个应用中没用到,所以方法里没具体代码*/	add: function(){},	remove: function(){},	getChild: function(){},	/*实现的方法*/	getElement: function(){		return this.element;	},	show: function(){		this.element.style.display = 'block';	}};/*命令类*这里的命令类非常简单。其构造函数的参数就是将被作为操作而调用的方法*/var MenuCommand = function(action){	this.action = action;};MenuCommand.prototype.execute = function(){	this.action();}/*汇总*/var fileMenu = new Menu('File');var openCommand = new MenuCommand(FileActions.open);var closeCommand = new MenuCommand(FileActions.close);var saveCommand = new MenuCommand(FileActions.save);var saveAsCommand = new MenuCommand(FileActions.saveAs);fileMenu.add(new MenuItem('open', openCommand));fileMenu.add(new MenuItem('close', closeCommand));fileMenu.add(new MenuItem('save', saveCommand));fileMenu.add(new MenuItem('saveas', saveAsCommand ));var editMenu = new Menu('Edit');var cutCommand = new MenuCommand(EditActions.cut);var copyCommand = new MenuCommand(EditActions.copy);var pasteCommand = new MenuCommand(EditActions.paste);var deleteCommand = new MenuCommand(EditActions.del);editMenu.add(new MenuItem('cut', cutCommand));editMenu.add(new MenuItem('copy', copyCommand));editMenu.add(new MenuItem('paste', pasteCommand));editMenu.add(new MenuItem('delete', deleteCommand));var insertMenu = new Menu('Insert');var textBlockCommand = new MenuCommand(InsertActions.textBlock);var imgCommand = new MenuCommand(InsertActions.image);insertMenu.add(new MenuItem('Text Block', textBlockCommand));insertMenu.add(new MenuItem('Image', imgCommand));var helpMenu = new Menu('Help');var showHelpCommand = new MenuCommand(HelpActions.showHelp);helpMenu.add(new MenuItem('Show Help', showHelpCommand));var appMenuBar = new MenuBar();appMenuBar.add(fileMenu);appMenuBar.add(editMenu);appMenuBar.add(insertMenu);document.getElementsByTagName('body')[0].appendChild(appMenuBar.getElement());appMenuBar.show();

第二个例子:

这是一个简单的html5游戏,有四个方向控制按钮和一个撤销按钮。每次点击方向按钮,会从当前位置向指定方向画出一条固定长度的线段。点击撤销按钮则可以撤销上一步操作。

撤销操作中,由于在canvas上画线的操作是不可逆的,即从A到B画一条线并不是简单的从B到A再画一条线。取消操作的唯一办法就是保存每一步操作的日志,在撤销时把记录过的操作(排除除最近一个操作)从头依次全部执行一遍。

/*接口定义*/var ReversibleCommand = new Interface('ReversibleCommand', ['execute']);/*命令对象类*/var MoveUp = function(cursor){	this.cursor = cursor;};MoveUp.prototype = {	execute: function(){		this.cursor.move(0, -10);	}};var MoveDown = function(cursor){	this.cursor = cursor;};MoveDown.prototype = {	execute: function(){		this.cursor.move(0, 10);	}};var MoveLeft = function(cursor){	this.cursor = cursor;};MoveLeft.prototype = {	execute: function(){		this.cursor.move(-10, 0);	}};var MoveRight = function(cursor){	this.cursor = cursor;};MoveRight.prototype = {	execute: function(){		this.cursor.move(10, 0);	}};var Undo = function(cursor){	this.cursor = cursor;};Undo.prototype = {	execute: function(){		this.cursor.undo();	}}/*接收者类,实现具体的操作*/var Cursor = function(width, height, parent){	this.width = width;	this.height = height;	this.position = {		x: width/2,		y: height/2	};	this.commandStack = [];	this.canvas = document.createElement('canvas');	this.canvas.width = this.width;	this.canvas.height = this.height;	parent.appendChild(this.canvas);	this.ctx = this.canvas.getContext('2d');	this.ctx.fillStyle = '#CCC000';	this.move(0, 0);};Cursor.prototype = {	move: function(x, y){		var _this = this;		/*先记录整个命令*/		this.commandStack.push(function(){			_this.lineTo(x, y);		});		/*再执行这个命令*/		_this.lineTo(x, y);	},	lineTo: function(x, y){		this.ctx.save();		this.ctx.beginPath();		this.ctx.fillStyle = '#CCC000';		this.ctx.moveTo(this.position.x, this.position.y);		this.position.x += x;		this.position.y += y;		this.ctx.lineTo(this.position.x, this.position.y);		this.ctx.stroke();		this.ctx.closePath();		this.ctx.restore();	},	executeCommands: function(){		this.position = {			x: this.width/2,			y: this.height/2		};		this.ctx.clearRect(0, 0, this.width, this.height);		for(var i=0; i

这里的undo操作也可以不用命令模式,只要像下面这样修改,感受一下:

/*1.修改 UndoButton类定义*/var UndoButton = function(label, parent, cursor){        /*这里没传入命令对象,不做接口检查,而是直接传入了接收者对象*/	this.element = document.createElement('button');	this.element.innerHTML = label;	parent.appendChild(this.element);	this.element.addEventListener('click', function(e){		cursor.undo();/*这里直接与接收者对象绑定,不是通过统一的execute方法对接*/	});};/*2.修改UndoButton实例化时传入的参数*/var undoButton = new UndoButton('Undo', body, cursor);/*3.去掉undo命令类的定义和实例化,也就是删掉下面的代码*/var Undo = function(cursor){	this.cursor = cursor;};Undo.prototype = {	execute: function(){		this.cursor.undo();	}}var undoCommand = new Undo(cursor);

两个例子通用的html部分,只需要一个body标签

Document

转载于:https://my.oschina.net/codespring/blog/607511

你可能感兴趣的文章
数据库之MySQL
查看>>
2019/1/15 批量删除数据库相关数据
查看>>
数据类型的一些方法
查看>>
Mindjet MindManager 2019使用教程:
查看>>
游戏设计的基本构成要素有哪些?
查看>>
详解 CSS 绝对定位
查看>>
AOP
查看>>
我的友情链接
查看>>
NGUI Label Color Code
查看>>
.NET Core微服务之基于Polly+AspectCore实现熔断与降级机制
查看>>
vue组件开发练习--焦点图切换
查看>>
浅谈OSI七层模型
查看>>
Webpack 2 中一些常见的优化措施
查看>>
移动端响应式
查看>>
python实现牛顿法求解求解最小值(包括拟牛顿法)【最优化课程笔记】
查看>>
js中var、let、const的区别
查看>>
腾讯云加入LoRa联盟成为发起成员,加速推动物联网到智联网的进化
查看>>
从Python2到Python3:超百万行代码迁移实践
查看>>
Windows Server已可安装Docker,Azure开始支持Mesosphere
查看>>
简洁优雅地实现夜间模式
查看>>