设计模式:TypeScript中的命令模式

​解耦调用者和接收者,并平滑不同接收者之间的差异。允许您轻松添加不同的命令来添加不同的功能。

欢迎来到 TypeScript 设计模式系列,该系列介绍了使用 TypeScript 进行 Web 开发的一些有用的设计模式。

 之前的文章如下:

设计模式对于 web 开发人员非常重要,掌握它们可以让我们写出更好的代码。在本文中,我将使用 TypeScript 来介绍命令模式。

iOS和macOS上有一个内置的快捷方式应用程序。使用这个程序,用户可以快速执行1个或多个任务。例如,快速发送消息、翻译文本、缩短URL和下载文件等任务。

接下来,让我们实现一个类似的应用程序。在下面的代码中,我们定义了一个  Shortcuts  类,在其中我们创建了 5 个成员方法,如  openUrl 、 shortenUrl 、 sendMessage  等。

class Shortcuts {  openUrl(url: string) {    console.log(`Open url: ${url}`);  }  shortenUrl(url: string) {    console.log(`Shorten url: ${url}`);  }  sendMessage(msg: string) {    console.log(`Send message: ${msg}`);  }  translateText(originText: string) {    console.log(`Translate text: ${originText}`);  }  downloadFile(fileUrl: string) {    console.log(`Download file: ${fileUrl}`);  }}

有了 Shortcuts 类,我们定义了一个 UIEventHandler 类,它包含了一个 handleAction 成员方法来处理用户行为事件。

class UIEventHandler {  constructor(public shortcuts: Shortcuts) {}  handleAction(action: ShortcutsMethods, arg: string) {    this.shortcuts[action](arg);  }}// "openUrl" | "shortenUrl" | "sendMessage" | "translateText" | "downloadFile"type ShortcutsMethods = Methods<Shortcuts>;type Methods<T> = {  [P in keyof T]: T[P] extends (...args: any) => void ? P : never;}[keyof T];

 handleAction  方法接收两个参数: action  和  arg  。 action  参数的类型是  ShortcutsMethods  ,它是通过  Methods  实用工具类型生成的。这个实用工具类型内部使用 TypeScript 映射类型。如果你想了解更多关于映射类型的信息,我建议你阅读这篇文章。

像专业人士一样使用 TypeScript 映射类型

文章地址:

https://mp.weixin.qq.com/s?__biz=MzU3NjM0NjY0OQ==&mid=2247484965&idx=1&sn=3e95787a17188b63769934fc42c5d72f&chksm=fd140953ca638045cf19f7915e04221ff2bc7d24aaa83f752b430e01d08c37baacf277e24876&token=797066188&lang=zh_CN#rd


有了  UIEventHandler  类,我们可以用以下方式使用它:

const shortcuts = new Shortcuts();const eventHandler = new UIEventHandler(shortcuts);eventHandler.handleAction("openUrl", "https://medium.com/@bytefer");eventHandler.handleAction("sendMessage", "Hello Bytefer!");

对于之前的代码,看起来没有问题,但是仔细分析后,你会发现以下问题:

  • 当调用 handleAction 方法时,我们需要确保 action 名称与 Shortcuts 类中的方法名称一致。

  • 随着函数的不断增加, Shortcuts  类中对应的方法也会越来越多,因此我们需要不断地修改  Shortcuts  类。


那么,我们应该如何处理上述问题呢?对于这个问题,我们可以使用命令模式。为了更好地理解下面的代码,让我们先看看相应的 UML 图:

事实上,我们可以将发送消息、翻译文本和缩短URL等任务包装到单独的命令中。

interface Command {  name: string;  execute(args: any): any;}

在上面的代码中,我们使用  interface  关键字定义了  Command  类型。在  Command  类型中,定义了一个  execute  方法来封装每个命令需要执行的逻辑。使用  Command  接口,让我们定义具体的命令。

class OpenUrlCommand implements Command {  name = "openUrl";  execute(args: any) {    console.log(`Open url: ${args[0]}`);  }}class SendMessageCommand implements Command {  name = "sendMessage";  execute(args: any) {    console.log(`Send message: ${args[0]}`);  }}

在上面的代码中,我们创建了 OpenUrlCommand 和 SendMessageCommand 类,在将来,我们的命令会不断增加,为了方便对不同命令类的管理,我们需要定义一个管理命令的类:

class CommandManager {  commands: Record<string, Command> = {};  registerCommand(name: string, command: Command) {    this.commands[name] = command;  }  executeCommand(command: string | Command, ...args: any) {    if (typeof command === "string") {      this.commands[command].execute(args);    } else {      command.execute(args);    }  }}

在 CommandManager 类中, registerCommand 方法用于注册命令。而 executeCommand 方法用于执行命令。使用 CommandManager 类,让我们更新之前创建的 UIEventHandler 类。

class UIEventHandler {  constructor(public cmdManager: CommandManager) {}  handleAction(command: string | Command, arg: string) {    this.cmdManager.executeCommand(command, arg);  }}

更新  UIEventHandler  类之后,让我们验证它的功能。

const commandManager = new CommandManager();commandManager.registerCommand("openUrl", new OpenUrlCommand());commandManager.registerCommand("msg", new SendMessageCommand());const eventHandler = new UIEventHandler(commandManager);eventHandler.handleAction("openUrl", "https://medium.com/@bytefer");eventHandler.handleAction("msg", "Hello Medium!");eventHandler.handleAction(new SendMessageCommand(), "Hello Bytefer!");

在上面的代码中,我们首先创建了  CommandManager  对象,然后注册了 2 个命令,然后我们创建了一个  UIEventHandler  对象,并使用对象的  handleAction  方法来执行注册的命令。在上述代码成功执行后,控制台将输出以下信息:

Open url: https://medium.com/@byteferSend message: Hello Medium!Send message: Hello Bytefer!

在富文本编辑器和命令行应用的上下文中,命令模式也经常被使用,例如,CAC,一个用于创建命令行应用的第三方库,内部也使用了命令模式,如果你感兴趣,可以阅读相应的源代码。

最后,让我们总结一下命令模式的使用场景:

  • 当需要抽象各种执行动作时,使用不同的参数来确定执行哪个动作。

  • 系统需要对请求调用者和请求接收者进行解耦,使调用者和接收者不直接交互,请求的调用者不需要知道接收者的存在,也不需要知道接收者是谁,接收者也不需要关心什么时候被调用。

 欢迎关注公众号:文本魔术,了解更多

  • 31
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值