设计模式:命令模式(Command Pattern)及实例

command,pattern · 浏览次数 : 0

小编点评

命令模式(Command Pattern)是一种行为设计模式,它允许你将一个请求封装为一个对象,从而使你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。命令模式的关键在于引入了抽象命令接口,使得发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。 在命令模式中,有以下几个角色: 1. **命令(Command)**:定义了一个执行操作的接口,声明了执行方法。 2. **具体命令(ConcreteCommand)**:实现了命令接口的对象,通常是“虚”的实现,会持有一个接收者,并调用接收者的功能来完成命令要执行的操作。 3. **接收者(Receiver)**:真正执行命令的对象,任何类都可以成为一个接收者,只要它能够实现命令要求实现的相应功能。 4. **调用者(Invoker)**:是要求命令对象执行请求的对象,通常会持有命令对象,可以持有很多的命令对象。 5. **客户(Client)**:创建具体的命令对象,并且设置命令对象的接收者。 命令模式的主要优点包括降低对象之间的耦合度,新的命令可以很容易地加入系统中,可以比较容易地设计一个组合命令,以及可以通过调用同一方法实现不同的功能。 使用命令模式可能会导致某些系统有过多的具体命令类,但这也是命令模式的优势之一,因为它允许你针对每一个具体的使用场景创建一个新的命令类。

正文

 好家伙,

 

0.什么是命令模式

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。

但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。

在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象实现二者之间的松耦合。这就是命令模式(Command Pattern)。

 

 0.1.角色解释

Command(定义接口): 定义命令的接口,声明执行的方法。

ConcreteCommand(实现接口):命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

Receiver(接收者):接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

Invoker(调用者):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。

    这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

Client(具体命令):创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者,或许,

    把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行

 

0.2.模式分析

1.命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开
2.每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作
3.命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
4.命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
5.命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联
 

0.3.模式优点

1.降低对象之间的耦合度。
2.新的命令可以很容易地加入到系统中。
3.可以比较容易地设计一个组合命令。
4.调用同一方法实现不同的功能
 

0.4.缺点

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
 
                                                          ----以上内容均来自“百度百科”
 

 

1.实例

想象一个实例

餐厅点餐案例

现在我走进一家餐厅,向一名服务员点餐,点了一份牛肉汉堡,

服务员将这份”牛肉汉堡"订单交给厨师,厨师制作这份牛肉牛肉汉堡

来个代码实现

// 厨师类
class Chef {
  makeBurger() {
    console.log("This is your burger.");
  }
}

// 服务员类
class Waitress {
  constructor(chef) {
    this.chef = chef;
  }

  takeOrder(order) {
    // 服务员接收订单并处理
    console.log("Waitress is taking the order for a " + order);
    this.chef.makeBurger(); // 直接通知厨师制作汉堡
  }
}

// 客户端代码
(() => {
  const chef = new Chef();
  const waitress = new Waitress(chef);
  
  // 客户点餐 服务员通知
  waitress.takeOrder("burger");
})();

 

那么如果我使用命令模式去实现呢?

// 命令接口(抽象概念,使用ES6的类来表示)
class Command {
  execute() {
    // 抽象方法execute,将在具体命令中实现
    throw new Error('You should implement the execute method');
  }
}

// 具体命令类
class OrderBurgerCommand extends Command {
  constructor(receiver) {
    super();
    this.receiver = receiver;
  }

  execute() {
    this.receiver.makeBurger();
  }
}

// 接收者类
class Chef {
  makeBurger() {
    console.log("Chef is making a burger.");
  }
}

// 请求者类
class Waitress {
  constructor() {
    this.commands = [];
  }

  takeOrder(command) {
    if (!(command instanceof Command)) {
      throw new Error('You can only take order of Command instances');
    }
    this.commands.push(command);
  }

  notify() {
    this.commands.forEach(command => command.execute());
  }
}

// 客户类
class Customer {
  constructor() {
    this.waitress = new Waitress();
    this.chef = new Chef();
    this.burgerCommand = new OrderBurgerCommand(this.chef);
  }

  orderBurger() {
    this.waitress.takeOrder(this.burgerCommand);
  }

  serveOrder() {
    this.waitress.notify();
  }
}

// 客户端代码
(() => {
  const customer = new Customer();
  customer.orderBurger();  // 客户点餐
  customer.serveOrder();   // 服务员通知厨师制作汉堡
})();

 

 

2.增加需求

同样的,如果我们新下单一条“薯条”

使用命令模式

// 命令模式实现

// 命令接口
class Command {
    execute() {
        // 抽象方法,需要在子类中实现
    }
}

// 汉堡命令
class BurgerCommand extends Command {
    constructor(chef) {
        super();
        this.chef = chef;
    }

    execute() {
        this.chef.makeBurger();
    }
}

// 薯条命令
class FrenchFriesCommand extends Command {
    constructor(chef) {
        super();
        this.chef = chef;
    }

    execute() {
        this.chef.makeFrenchFries();
    }
}

// 厨师类
class Chef {
    makeBurger() {
        console.log('制作汉堡');
    }

    makeFrenchFries() {
        console.log('制作薯条');
    }
}

// 服务员类
class Waiter {
    constructor() {
        this.commands = [];
    }

    order(command) {
        this.commands.push(command);
        console.log('订单已接收');
    }

    serve() {
        this.commands.forEach(command => command.execute());
    }
}

// 客户类
class Customer {
    constructor(waiter) {
        this.waiter = waiter;
    }

    orderBurger() {
        const chef = new Chef();
        const burgerCommand = new BurgerCommand(chef);
        this.waiter.order(burgerCommand);
    }

    orderFrenchFries() {
        const chef = new Chef();
        const friesCommand = new FrenchFriesCommand(chef);
        this.waiter.order(friesCommand);
    }
}

// 客户端代码
(() => {
    const waiter = new Waiter();
    const customer = new Customer(waiter);

    // 客户点一份汉堡
    customer.orderBurger();

    // 客户再点一份薯条
    customer.orderFrenchFries();

    // 服务员开始服务
    waiter.serve();
})();

再结合以上代码来看,命令模式的优势有

1.真正实现了解耦:客户不需要知道汉堡制作的细节,服务员也不需要知道汉堡制作的细节,客户仅仅是下单,服务员仅仅是通知

2.易于扩展:如果需要添加新的操作(如包装、加热),可以创建新的命令类,而无需修改现有的类结构。

 

通过汉堡和薯条的例子,我们可以看到命令模式如何使得代码更加灵活、可维护,并且更容易进行扩展。

 

与设计模式:命令模式(Command Pattern)及实例相似的内容:

设计模式:命令模式(Command Pattern)及实例

好家伙, 0.什么是命令模式 在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。 但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。 在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合

软件设计模式系列之十六——命令模式

命令模式(Command Pattern)是一种行为型设计模式,旨在将请求发送者和接收者解耦,将一个请求封装为一个对象,从而允许您参数化客户端对象以进行不同的请求、排队请求或记录请求,并支持可撤销操作。 命令模式的核心思想是将一个请求包装成一个对象,包括请求的参数和接收者对象,然后客户端只需要调用该...

实践GoF的23种设计模式:命令模式

摘要:命令模式可将请求转换为一个包含与请求相关的所有信息的对象, 它能将请求参数化、延迟执行、实现 Undo / Redo 操作等。 本文分享自华为云社区《【Go实现】实践GoF的23种设计模式:命令模式》,作者:元闰子。 简介 现在的软件系统往往是分层设计。在业务层执行一次请求时,我们很清楚请求的

设计模式学习(二十一):命令模式

设计模式学习(二十一):命令模式 作者:Grey 原文地址: 博客园:设计模式学习(二十一):命令模式 CSDN:设计模式学习(二十一):命令模式 命令模式 命令模式是一种行为型模式。 通过调用者调用接受者执行命令,执行顺序是: 调用者→命令→接受者 ```, 如下示例,CopyCommand 中的

Netty-介绍-1

Netty介绍和应用场景 要求 已经掌握了 主要技术构成: Java OOP 编程、 Java 多线程编程、 Java IO 编程 、 Java 网络编程、 常用的Java 设计模式(比如 观察者模式 ,命令模式,职责链模式 )、 常用的数据结构(比如 链表) Netty的介绍 1、Netty 是由

[转帖]完整阿里云Redis开发规范

https://www.cnblogs.com/-wenli/p/10941249.html 本文主要介绍在使用阿里云Redis的开发规范,从下面几个方面进行说明。 键值设计 命令使用 客户端使用 相关工具 删除bigkey 通过本文的介绍可以减少使用Redis过程带来的问题。 一、键值设计 1、k

RCE(Pikachu)

作用 可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。 原理 远程系统命令执行 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口 比如我们常见的路由器、防火墙、入侵检测等设备的web管理界面上 一般会给用户提供一个ping操作的web界面,用

企业生产环境中的麒麟V10(ARM架构)操作系统部署jdk和redis三主三从交叉版集群

前言:麒麟ARM操作系统是国企和政务机关推行信创化选择率比较高的一款操作系统,然而ARM操作系统非主流的X86系统,除了命令一样,在架构方面差别极大,初次接触多多少少会踩坑,下面我将在公司中部署的实例列举出来,供大家参考,ip和设计机密信息不方便展示,统用虚拟信息代替。 经过多次验证,用了多种通用版

[转帖]阿里云Redis开发规范

https://developer.aliyun.com/article/531067?spm=a2c6h.13262185.0.0.59cb5a7cO5qJMS#cc2 简介: 本文介绍了在使用阿里云Redis的开发规范,从键值设计、命令使用、客户端使用、相关工具等方面进行说明,通过本文的介绍可以

httpsok-v1.11.0支持CDN证书自动部署

httpsok-v1.11.0支持CDN证书自动部署 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具,专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业,稳定、安全、可靠。 一行命令,一分钟轻松搞定SSL证书自动续期 v1.11.0 版本新特性 ✅修复某些情