14-Python與設計模式--命令模式
一、飯店點餐系統
又是一個點餐系統(原諒作者的吃貨屬性)。不過這次的點餐系統是個飯店的點餐系統。飯店的點餐系統有什麼不同嘛?大夥想想看,在大多數飯店中,當服務員已經接到顧客的點單,錄入到系統中後,根據不同的菜品,會有不同的後臺反應。比如,飯店有冷盤間、熱菜間、主食間,那當服務員將菜品錄入到系統中後,冷盤間會打印出顧客所點的冷盤條目,熱菜間會打印出顧客所點的熱菜條目,主食間會打印出主食條目。那這個系統的後臺模式該如何設計?當然,直接在場景程式碼中加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:Nofify..." 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介面。這樣,後臺三個命令類就可以直接繼承,不用進行修改了。(這裡子系統沒有變動,可以將三個子系統的命令廢棄不用,直接用foodCommand嗎?當然可以,各有利蔽。請讀者結合自身開發經驗,進行思考相對於自己業務場景的使用,哪種方式更好。)
為使場景業務精簡一些,我們再加一個選單類來輔助業務,選單類在本例中直接寫死。
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:Nofify...
HOTDISH:Cook Yu-Shiang Shredded Pork
HOTDISH:Cook Sauteed Tofu, Home Style
COOLDISH:Cook Cucumber
MAINFOOD:Cook Rice
二、命令模式
命令模式的定義為:將一個請求封裝成一個物件,從而可以使用不同的請求將客戶端引數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能。命令模式中通常涉及三類物件的抽象:Receiver,Command,Invoker(本例中的waiterSys)。
只有一個Invoker的命令模式也可以抽象成一個類似的“星形網路”,但與之前介紹的中介者模式不同,單純的命令模式更像是一個輻射狀的結構,由Invoker直接對Receiver傳遞命令,而一般不反向傳遞,中介者模式“星形網路”的中心,是個協調者,抽象結節間的資訊流全部或者部分是雙向的。
另外,命令模式的定義中提到了“撤銷和恢復功能”,也給了各位開發人員一個命令模式使用過程中的建議:各個Receiver中可以設計一個回滾介面,支援命令的“撤銷”。
三、命令模式的優點和應用場景
優點:
1、低耦合:呼叫者和接收者之間沒有什麼直接關係,二者通過命令中的execute介面聯絡;
2、擴充套件性好:新命令很容易加入,也很容易拼出“組合命令”。
應用場景:
1、觸發-反饋機制的系統,都可以使用命令模式思想。如基於管道結構的命令系統(如SHELL),可以直接套用命令模式;此外,GUI系統中的操作反饋(如點選、鍵入等),也可以使用命令模式思想。
四、命令模式的缺點
1、如果業務場景中命令比較多,那麼對應命令類和命令物件的數量也會增加,這樣系統會膨脹得很大。