1. 程式人生 > >14-Python與設計模式--命令模式

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)。


f1.png


只有一個Invoker的命令模式也可以抽象成一個類似的“星形網路”,但與之前介紹的中介者模式不同,單純的命令模式更像是一個輻射狀的結構,由Invoker直接對Receiver傳遞命令,而一般不反向傳遞,中介者模式“星形網路”的中心,是個協調者,抽象結節間的資訊流全部或者部分是雙向的。
另外,命令模式的定義中提到了“撤銷和恢復功能”,也給了各位開發人員一個命令模式使用過程中的建議:各個Receiver中可以設計一個回滾介面,支援命令的“撤銷”。

三、命令模式的優點和應用場景

優點:

1、低耦合:呼叫者和接收者之間沒有什麼直接關係,二者通過命令中的execute介面聯絡;
2、擴充套件性好:新命令很容易加入,也很容易拼出“組合命令”。

應用場景:

1、觸發-反饋機制的系統,都可以使用命令模式思想。如基於管道結構的命令系統(如SHELL),可以直接套用命令模式;此外,GUI系統中的操作反饋(如點選、鍵入等),也可以使用命令模式思想。

四、命令模式的缺點

1、如果業務場景中命令比較多,那麼對應命令類和命令物件的數量也會增加,這樣系統會膨脹得很大。