源碼地址: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
優點:
缺點:
適用性:
系統需要將請求調用者和請求接收者解耦,使得調用者和接收者不直接交互。
在不同的時刻指定、排列和執行請求。
支持修改日志、撤銷操作。
系統需要將一組操作組合在一起,即支持宏命令。