目录定义结构实例自定义快捷键撤销与重做录制与回放宏命令总结定义 Encapsulate a request as an object, thereby letting you par
Encapsulate a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests,and support undoable operations.“
「命令模式」将「请求」封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,同时支持可撤消的操作。
这里的「请求」的定义,并不是我们前端常说的「ajax 请求」,而是一个「动作请求」,也就是发起一个行为。例如,通过遥控器关闭电视,这里的「关闭」就是一个请求。在命令模式中,我们将请求抽象成一个命令,这个命令是可复用的,它只关心它的接受者(电视);而对于动作的发起者(遥控器)来说,它只关心它所支持的命令有哪些,而不关心这些命令具体是做什么的。
命令模式的类图如下:
在该类图中,我们看到五个角色:
Reciver 与 Invoker 没有耦合,当需要拓展功能时,通过新增 Command,因此命令模式符合开闭原则。
自定义快捷键是一个编辑器的最基本功能。通过命令模式,我们可以写出一个将键位与键位逻辑解耦的结构。
interface Command {
exec():void
}
type Keymap = { [key:string]: Command }
class HoTKEy {
keymap: Keymap = {}
constructor(keymap: Keymap) {
this.keymap = keymap
}
call(e: KeyboardEvent) {
const prefix = e.ctrlKey ? 'ctrl+' : ''
const key = prefix + e.key
this.dispatch(key)
}
dispatch(key: string) {
this.keymap[key].exec()
}
}
class CopyCommand implements Command {
constructor(clipboard: any) {}
exec() {}
}
class CutCommand implements Command {
constructor(clipboard: any) {}
exec() {}
}
class PasteCommand implements Command {
constructor(clipboard: any) {}
exec() {}
}
const clipboard = { data: '' }
const keymap = {
'ctrl+x': new CutCommand(clipboard),
'ctrl+c': new CopyCommand(clipboard),
'ctrl+v': new PasteCommand(clipboard)
}
const hotkey = new Hotkey(keymap)
document.onkeydown = (e) => {
hotkey.call(e)
}
在本例中,hotkey是 Invoker,clipboard是 Receiver。当我们需要修改已有的 keymap 时,只需要新增或替换已有的key或Command即可。
是不是觉得这个写法似曾相识?没错Redux 也是应用了命令模式,Store 相当于 Receiver,Action 相当于 Command,Dispatch 相当于 Invoker。
基于命令模式,我们可以很容易拓展,使它支持撤销与重做。
interface IPerson {
moveTo(x: number, y: number): void
}
class Person implements Person {
x = 0
y = 0
moveTo(x: number, y: number) {
this.x = x
this.y = y
}
}
interface Command {
exec(): void
undo(): void
}
class MoveCommand implements Command {
prevX = 0
prevY = 0
person: Person
constructor(person: Person) {
this.person = person
}
exec() {
this.prevX = this.person.x
this.prevY = this.person.y
this.person.moveTo(this.prevX++, this.prevY++)
}
undo() {
this.person.moveTo(this.prevX, this.prevY)
}
}
const ezio = new Person()
const moveCommand = new MoveCommand(ezio)
moveCommand.exec()
console.log(ezio.x, ezio.y)
moveCommand.undo()
console.log(ezio.x, ezio.y)
想想我们在游戏中的录制与回放功能,如果将角色的每个动作都作为一个命令的话,那么在录制时就能够得到一连串的命令队列。
class Control {
commands: Command[] = []
exec(command) {
this.commands.push(command)
command.exec(this.person)
}
}
const ezio = new Person()
const control = new Control()
control.exec(new MoveCommand(ezio))
control.exec(new MoveCommand(ezio))
console.log(control.commands)
当我们有了命令队列,我们又能够很容易得进行多次的撤销和重做,实现一个命令的历史记录。只需要移动当前命令队列的指针即可。
class CommandHistory {
commands: Command[] = []
index = 0
get currentCommand() {
return this.commands[index]
}
constructor(commands: Command[]) {
this.commands = commands
}
redo() {
this.index++
this.currentCommand.exec()
}
undo() {
this.currentCommand.undo()
this.index--
}
}
同时,如果我们将命令序列化成一个对象,它便可以用于保存与传递。这样我们将它发送到远程计算机,就能实现远程控制ezio移动的功能。
[{
type: 'move',
x: 1,
y: 1,
}, {
type: 'move',
x: 2,
y: 2,
}]
对Command进行一些简单的处理就能够将已有的命令组合起来执行,将其变成一个宏命令。
class BatchedCommand implements Command {
commands = []
constructor(commands) {
this.commands = commands
}
exec() {
this.commands.forEach(command => command.exec())
}
}
const batchedMoveCommand = new BatchedCommand([
new MoveCommand(ezio),
new SitCommand(ezio),
])
batchedMoveCommand.exec()
通过以上几个例子,我们可以看出命令模式有一下几个特点:
以上就是详解javascript实践中的命令模式的详细内容,更多关于Javascript命令模式的资料请关注编程网其它相关文章!
--结束END--
本文标题: 详解Javascript实践中的命令模式
本文链接: https://lsjlt.com/news/125128.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0