源码地址:https://github.com/weilanhanf/PythonDesignPatterns
说明:
命令在发送方被激活,而在接收方被响应。一个对象既可以作为命令的发送方,也可以作为命令的接收方,或者都可以。命令的典型应用就是图形用户界面开发。每一个窗体都包含菜单,工具栏,按钮等控件,将用户的单机动作也叫命令作为外部事件,然后系统会根据绑定的事件处理程序执行相应的动作即命令获得响应,完成用户发出的请求。
大多数情况下,命令的调用者和接收者是前期绑定的,但是想要对命令进行操作的时候,就需要撤销命令的调用者和接收者之间的紧密耦合关系,使得二者相互独立。
命令模式(Command Pattern):将请求封装成对象,将其作为命令调用者和接收者的中介,而抽象出来的命令对象又使得能够对一系列请求进行特殊操作,比如:对请求排队或记录请求日志,以及支持可撤消的操作.
结构组成:
命令角色,具体命令角色,接收者角色,请求者(调用者)角色(Invoker),客户角色(Client)
实例:
在大多数饭店中,当服务员已经接到顾客的点单,录入到系统中后,根据不同的菜品,会有不同的后台反应。比如,饭店有凉菜间、热菜间、主食间,那当服务员将菜品录入到系统中后,凉菜间会打印出顾客所点的凉菜条目,热菜间会打印出顾客所点的热菜条目,主食间会打印出主食条目。那这个系统的后台模式该如何设计?当然,直接在场景代码中加if…else…语句判断是个方法,可这样做又一次加重了系统耦合,违反了单一职责原则,遇到系统需求变动时,又会轻易违反开闭原则。所以,我们需要重新组织一下结构。可以将该系统设计成前台服务员系统和后台系统,后台系统进一步细分成主食子系统,凉菜子系统,热菜子系统。
#主食子系统,凉菜子系统,热菜子系统,后台三个子系统 class backSys(): def cook(self,dish): pass class mainFoodSys(backSys): def cook(self,dish): print("MAINFOOD:Cook %s"%dish) class coolDishSys(backSys): def cook(self,dish): print("COOLDISH:Cook %s"%dish) class hotDishSys(backSys): def cook(self,dish): print("HOTDISH:Cook %s"%dish) #前台服务员系统与后台系统的交互,我们可以通过命令的模式来实现, #服务员将顾客的点单内容封装成命令,直接对后台下达命令,后台完成命令要求的事, #前台系统构建如下 class waiterSys(): menu_map=dict() commandList=[] def setOrder(self,command): print("WAITER:Add dish") self.commandList.append(command) def cancelOrder(self,command): print("WAITER:Cancel order...") self.commandList.remove(command) def notify(self): print("WAITER:Notify...") for command in self.commandList: command.execute() #前台系统中的notify接口直接调用命令中的execute接口,执行命令。命令类构建 class Command(): receiver = None def __init__(self, receiver): self.receiver = receiver def execute(self): pass class foodCommand(Command): dish="" def __init__(self,receiver,dish): self.receiver=receiver self.dish=dish def execute(self): self.receiver.cook(self.dish) class mainFoodCommand(foodCommand): pass class coolDishCommand(foodCommand): pass class hotDishCommand(foodCommand): pass """ Command类是个比较通过的类,foodCommand类是本例中涉及的类,相比于Command类进行了一定的改造。 由于后台系统中的执行函数都是cook,因而在foodCommand类中直接将execute接口实现, 如果后台系统执行函数不同,需要在三个子命令系统中实现execute接口。 这样,后台三个命令类就可以直接继承,不用进行修改了。 """ #菜单类辅助业务 class menuAll: menu_map=dict() def loadMenu(self):#加载菜单,这里直接写死 self.menu_map["hot"] = ["Yu-Shiang Shredded Pork", "Sauteed Tofu, Home Style", "Sauteed Snow Peas"] self.menu_map["cool"] = ["Cucumber", "Preserved egg"] self.menu_map["main"] = ["Rice", "Pie"] def isHot(self,dish): if dish in self.menu_map["hot"]: return True return False def isCool(self,dish): if dish in self.menu_map["cool"]: return True return False def isMain(self,dish): if dish in self.menu_map["main"]: return True return False #业务场景 if __name__=="__main__": dish_list=["Yu-Shiang Shredded Pork","Sauteed Tofu, Home Style","Cucumber","Rice"]#顾客点的菜 waiter_sys=waiterSys() main_food_sys=mainFoodSys() cool_dish_sys=coolDishSys() hot_dish_sys=hotDishSys() menu=menuAll() menu.loadMenu() for dish in dish_list: if menu.isCool(dish): cmd=coolDishCommand(cool_dish_sys,dish) elif menu.isHot(dish): cmd=hotDishCommand(hot_dish_sys,dish) elif menu.isMain(dish): cmd=mainFoodCommand(main_food_sys,dish) else: continue waiter_sys.setOrder(cmd) waiter_sys.notify()
打印结果:
WAITER:Add dish
WAITER:Add dish
WAITER:Add dish
WAITER:Add dish
WAITER:Notify...
HOTDISH:Cook Yu-Shiang Shredded Pork
HOTDISH:Cook Sauteed Tofu, Home Style
COOLDISH:Cook Cucumber
MAINFOOD:Cook Rice
优点:
降低对象之间的耦合度。
新的命令可以很容易地加入到系统中。
可以比较容易地设计一个组合命令。
调用同一方法实现不同的功能
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
适用性:
系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
在不同的时刻指定、排列和执行请求。
支持修改日志、撤销操作。
系统需要将一组操作组合在一起,即支持宏命令。