命令模式是与观察者模式都是行为型设计模式
的一种,行为型模式注重于事情的响应性
,当客户端触发一个事件之后,与操作相关的参数以对象的形式封装起来
,提交给相应的处理对象解析执行,这个封装起来了的对象包含了执行这个操作所需要的全部信息,比如:
比如一个安装向导程序
,用户在界面上进行一些列的选项操作之后…,这些选项会封装到一个或多个对象中,提交到一个对象管理器中,一系列配置选项完成之后,点击Install,对象管理器就根据之前传入的参数配置进行程序的安装,分别运行这些对象的execute
方法,具体执行程序的安装。
还有示例就是打印机后台处理程序,当选择打印功能之后,在配置打印内容、纸张大小、颜色…等等配置之后,这些配置会以对象的形式存储在Command
中,当点击打印是,就会执行Command的execute方法
执行打印操作
对于命令模式,其有以下一些术语Client、Receiver、Command、Invoker
Command
了解Receiver的具体细节,他是直接调用Receiver来执行相关命令的调用者,Command里面封装着执行这条指令或响应所需求的所有信息,在调用Receiver的时候直接提供给ReceiverReceiver
是具体执行这些功能的类,它具体执行需求,一个Reciver可能执行一种功能,也可能执行很多功能Client
针对于不同的响应去负责创建相应的Command对象,并为这个Command设置相应的执行功能的ReciverInvoker
能维护这些Command对象,能调用执行这些Command的execute
,并且能实现相应的回滚操作(比如shell的上键跳转上一条命令)
等其主要意图是:
示例
比如一个安装程序,用户在操作界面进行相应操作,这些操作被封装成一个对象保存到Wizard中去,当所有配置配置完了,用户点击install,执行execute方法对程序进行安装
# -*- coding=utf-8 -*-
class Wizard(object):
def __init__(self, src: str, rootdir: str):
self.choices = []
self.rootdir = rootdir
self.src = src
def preference(self, command):
self.choices.append(command)
def execute(self):
for choice in self.choices:
if list(choice.values())[0]:
print("Copying binaries --", self.src, "to", self.rootdir)
else:
print("No Operation")
if __name__ == "__main__":
# client code
wizard = Wizard("python.zip", "/usr/bin/")
# User choose to install Python only
wizard.preference({
"python": True})
wizard.preference({
"Java": False})
# user click install
wizard.execute()
Copying binaries -- python.zip to /usr/bin/
No Operation
Command
:声明了一个执行操作的接口
ConcreteCommand
:将一个Receiver对象和一个操作绑定在一起
Client
:创建ConcreteCommand对象并且将其跟对应的Receiver绑定在一起
Invoker
:要求改ConcreteCommand执行某个请求
Receiver
:知道某个特定的功能如何实现,需要什么
用代码了解就是
# -*- coding=utf-8 -*-
from abc import ABCMeta, abstractmethod
class Command(metaclass=ABCMeta):
def __init__(self, rece):
self.rece = rece
@abstractmethod
def execute(self):
pass
class ConcreteCommand(Command):
def execute(self):
self.rece.action()
class Receiver(object):
def action(self):
print("ConcreteCommand call the real receiver to do this requester")
class Invoker(object):
def command(self, cmd: Command):
self.cmd = cmd
def execute(self):
self.cmd.execute()
if __name__ == "__main__":
# client
receiver = Receiver()
command = ConcreteCommand(receiver)
invoker = Invoker()
invoker.command(command)
invoker.execute()
ConcreteCommand call the real receiver to do this requester
客户端创建ConcreteCommand并且为这个Command配置对应的Receiver对象,并将其提交到Invoker中,Invoker将command存入其维护的一个command队列中,能够实现调用和回滚等操作
以一个证券交易为例子,用户去买入和卖出股票,他是无法直接去证券交易所的,用户的售卖通过一个代理(Agent),这些需求存储在代理的ToDoList
中,当到特定的事件,这个代理会处理这些需求,在证券交易所进行相应的处理
对于用户的命令,它有一个基类Order
,里面定义了一个接口execute
,在此基础上实现两个子类ButStockOrder
用于买入股票和SellStockOrder
用于卖出票,这里Order对应Command
,实现的子类对应ConcreteCommand
以及具体实现这些操作的类StockTrade
,实现具体的响应,对应Receiver
,其里面有一个buy
方法和sell
方法,实现股票的买入和卖出
以及代理类Agent
,Agent对应Invoker
,其有一个placeOrder
用于加入响应,并且执行响应操作的功能
其实现如下
# -*- coding=utf-8 -*-
from abc import ABCMeta, abstractmethod
import datetime
class Order(metaclass=ABCMeta):
"""
package the user's order into class Order
"""
@abstractmethod
def execute(self):
pass
class BuyStockOrder(Order):
def __init__(self, stoc):
self.stoc = stoc
def execute(self):
date = datetime.date.today()
self.stoc.buy(date)
class SellStockOrder(Order):
def __init__(self, stoc):
self.stoc = stoc
def execute(self):
date = datetime.date.today() + datetime.timedelta(days=3)
self.stoc.sell(date)
class StockTrade(object):
def buy(self, date: str):
print(date, ":You buy the stock!")
def sell(self, date: str):
print(date, ":You sell the stock!")
class Agent(object):
def __init__(self):
self.__orderQueue = []
def placeOrder(self, order: Order):
self.__orderQueue.append(order)
order.execute()
if __name__ == "__main__":
# client
stock = StockTrade()
buyStock = BuyStockOrder(stock)
sellStock = SellStockOrder(stock)
# Invoker
agent = Agent()
agent.placeOrder(buyStock)
agent.placeOrder(sellStock)
2021-11-24 :You buy the stock!
2021-11-27 :You sell the stock!
#include
#include
using namespace std;
class StockTrade{
public:
StockTrade() = default;
public:
inline void buy(){
cout<<"You will buy the stock!"<trade = trade;
}
public:
void execute() override{
trade.buy();
}
private:
StockTrade trade;
};
class SellStockOrder: public Order{
public:
SellStockOrder(StockTrade trade){
this->trade = trade;
}
public:
void execute() override{
trade.sell();
}
private:
StockTrade trade;
};
class Agent{
public:
Agent(){
cmd_queue = vector();
}
public:
void placeOrder(Order *order){
cmd_queue.push_back(order);
order->execute();
}
private:
vector cmd_queue;
};
int main(int argc, char *argv[]){
// Client
StockTrade stock = StockTrade();
BuyStockOrder *buyStock = new BuyStockOrder(stock);
SellStockOrder *sellStock = new SellStockOrder(stock);
// Invoker
Agent agent = Agent();
agent.placeOrder(buyStock);
agent.placeOrder(sellStock);
}
You will buy the stock!
You will sell the stock!
优点:
- 将调用操作的类于知道如何执行该操作的对象进行解耦
- 提供队列系统之后,可以执行一系列命令
- 添加新命令容易,并且无需更改代码
- 可以实现类似于shell的回滚操作
缺点:
- 为了实现目标,需要大量的类和对象进行协作。
- 每一个单独的命令都是一个ConcreteCommand,需要维护和实现的类的数量很多