命令模式是将一次完整的业务处理拆成了提出业务和处理业务两部分,这个模式把提出业务称为发起命令,处理业务称为执行命令。同时,提出业务与处理业务的解耦,暗含着一种异步的思想。
“命令”一词,听起来很拽的感觉,调用方(Invoker)只管发起命令(Command),不参与命令如何被执行(Receiver)。既然这么拽,我们就拿皇帝的圣旨来做例子好了。首先,我们看下UML图:
等等,这是不是与书上和网上的UML图不一样?Client哪去了?我们先看看一般文章中的类图长什么样:
这个图往往容易误导读者,让人以为Client就是调用方。事实上Client只是提供了一个Invoker、Command和Receiver的场景,用Context来定义它更为合理。不管怎么说,场景类只是辅助,模式的本质还是第一张UML图。
下面我们用代码来实现。首先给出实际案例:公元前119年,汉武帝派卫青、霍去病两路大军,分别去灭掉匈奴主力与匈奴王庭。起初,汉武帝给出的命令是霍去病攻打匈奴主力,卫青负责匈奴王庭。然而在北征的途中,却是卫青遇到了匈奴主力,霍去病碰到了匈奴老窝。两人分别派信使通报了皇帝情况,皇帝则颁布了新的命令,将二人的攻打对象调换。
先定义两位Receiver(卫青、霍去病),以及他们的行为:
# 命令接收者 - 卫青
class WeiQingReceiver(Singleton):
def attack_main_force(self):
print('卫青:我现在去攻打匈奴主力')
def attack_palace(self):
print('卫青:我现在去攻打匈奴王庭')
def stop_attack_main_force(self):
print('卫青:我已停止攻打匈奴主力')
def stop_attack_palace(self):
print('卫青:我已停止攻打匈奴王庭')
# 命令接收者 - 霍去病
class HuoQuBingReceiver:
def attack_main_force(self):
print('霍去病:我现在去攻打匈奴主力')
def attack_palace(self):
print('霍去病:我现在去攻打匈奴王庭')
def stop_attack_main_force(self):
print('霍去病:我已停止攻打匈奴主力')
def stop_attack_palace(self):
print('霍去病:我已停止攻打匈奴王庭')
定义命令发布者Invoker(汉武帝):
# 汉武帝 - Invoker
class HanWuDiInvoker:
_command = None
def set_command(self, _command):
self._command = _command
def issue_command(self):
self._command.execute()
def undo_command(self):
self._command.undo()
接下来定义抽象命令类,及4个具体的命令类(卫青攻打王庭、卫青攻打主力、霍去病攻打王庭、霍去病攻打主力)
# 抽象命令类
class AbstractCommand:
def execute(self):
pass
def undo(self):
pass
# 具体命令类 - 卫青攻打匈奴主力
class WeiQingAttackMainForceCommand:
__weiqing = WeiQingReceiver()
def execute(self):
self.__weiqing.attack_main_force()
def undo(self):
self.__weiqing.stop_attack_main_force()
# 具体命令类 - 卫青攻打匈奴王庭
class WeiQingAttackPalaceCommand:
__weiqing = WeiQingReceiver()
def execute(self):
self.__weiqing.attack_palace()
def undo(self):
self.__weiqing.stop_attack_palace()
# 具体命令类 - 霍去病攻打匈奴主力
class HuoQuBingAttackMainForceCommand:
__huoqubing = HuoQuBingReceiver()
def execute(self):
self.__huoqubing.attack_main_force()
def undo(self):
self.__huoqubing.stop_attack_main_force()
# 具体命令类 - 霍去病攻打匈奴王庭
class HuoQuBingAttackPalaceCommand:
__huoqubing = HuoQuBingReceiver()
def execute(self):
self.__huoqubing.attack_palace()
def undo(self):
self.__huoqubing.stop_attack_palace()
最后构造场景:
if __name__ == '__main__':
hanWuDi = HanWuDiInvoker()
print('1.汉武帝发布命令:卫青攻打匈奴王庭,霍去病攻打匈奴主力。')
command1 = WeiQingAttackPalaceCommand()
hanWuDi.set_command(command1)
hanWuDi.issue_command()
command2 = HuoQuBingAttackMainForceCommand()
hanWuDi.set_command(command2)
hanWuDi.issue_command()
print()
print('2.卫青中途遇到了匈奴主力,请示皇帝后,皇帝颁发了新的命令:卫青攻打匈奴主力')
hanWuDi.set_command(command1)
hanWuDi.undo_command()
command1 = WeiQingAttackMainForceCommand()
hanWuDi.set_command(command1)
hanWuDi.issue_command()
print()
print('3.霍去病也随后请示皇帝,说自己到达了匈奴老窝,于是皇帝颁发了新的命令:霍去病攻打匈奴王庭')
hanWuDi.set_command(command2)
hanWuDi.undo_command()
command2 = HuoQuBingAttackPalaceCommand()
hanWuDi.set_command(command2)
hanWuDi.issue_command()
执行结果:
1.汉武帝发布命令:卫青攻打匈奴王庭,霍去病攻打匈奴主力。
卫青:我现在去攻打匈奴王庭
霍去病:我现在去攻打匈奴主力
2.卫青中途遇到了匈奴主力,请示皇帝后,皇帝颁发了新的命令:卫青攻打匈奴主力
卫青:我已停止攻打匈奴王庭
卫青:我现在去攻打匈奴主力
3.霍去病也随后请示皇帝,说自己到达了匈奴老窝,于是皇帝颁发了新的命令:霍去病攻打匈奴王庭
霍去病:我已停止攻打匈奴主力
霍去病:我现在去攻打匈奴王庭