Python與設(shè)計(jì)模式之訪問者模式

一、藥房業(yè)務(wù)系統(tǒng)

假設(shè)一個(gè)藥房,有一些大夫,一個(gè)藥品劃價(jià)員和一個(gè)藥房管理員,它們通過一個(gè)藥房管理系統(tǒng)組織工作流程。大夫開出藥方后,藥品劃價(jià)員確定藥品是否正常,價(jià)格是否正確;通過后藥房管理員進(jìn)行開藥處理。該系統(tǒng)可以如何實(shí)現(xiàn)?最簡單的想法,是分別用一個(gè)一個(gè)if…else…把劃價(jià)員處理流程和藥房管理流程實(shí)現(xiàn),這樣做的問題在于,擴(kuò)展性不強(qiáng),而且單一性不強(qiáng),一旦有新藥的加入或者劃價(jià)流程、開藥流程有些變動(dòng),會(huì)牽扯比較多的改動(dòng)。今天介紹一種解決這類問題的模式:訪問者模式。
首先,構(gòu)造藥品類和工作人員類:

class Medicine:
    name=""
    price=0.0
    def __init__(self,name,price):
        self.name=name
        self.price=price
    def getName(self):
        return self.name
    def setName(self,name):
        self.name=name
    def getPrice(self):
        return self.price
    def setPrice(self,price):
        self.price=price
    def accept(self,visitor):
        pass
class Antibiotic(Medicine):
    def accept(self,visitor):
        visitor.visit(self)
class Coldrex(Medicine):
    def accept(self,visitor):
        visitor.visit(self)

藥品類中有兩個(gè)子類,抗生素和感冒藥;

class Visitor:
    name=""
    def setName(self,name):
        self.name=name
    def visit(self,medicine):
        pass
class Charger(Visitor):
    def visit(self,medicine):
        print "CHARGE: %s lists the Medicine %s. Price:%s " % (self.name,medicine.getName(),medicine.getPrice())
class Pharmacy(Visitor):
    def visit(self,medicine):
        print "PHARMACY:%s offers the Medicine %s. Price:%s" % (self.name,medicine.getName(),medicine.getPrice())

工作人員分為劃價(jià)員和藥房管理員。
在藥品類中,有一個(gè)accept方法,其參數(shù)是個(gè)visitor;而工作人員就是從Visitor類中繼承而來的,也就是說,他們就是Visitor,都包含一個(gè)visit方法,其參數(shù)又恰是medicine。藥品作為處理元素,依次允許(Accept)Visitor對(duì)其進(jìn)行操作,這就好比是一條流水線上的一個(gè)個(gè)工人,對(duì)產(chǎn)品進(jìn)行一次次的加工。整個(gè)業(yè)務(wù)流程還差一步,即藥方類的構(gòu)建(流水線大機(jī)器)。

class ObjectStructure:
    pass
class Prescription(ObjectStructure):
    medicines=[]
    def addMedicine(self,medicine):
        self.medicines.append(medicine)
    def rmvMedicine(self,medicine):
        self.medicines.append(medicine)
    def visit(self,visitor):
        for medc in self.medicines:
            medc.accept(visitor)

藥方類將待處理藥品進(jìn)行整理,并組織Visitor依次處理。
業(yè)務(wù)代碼如下:

if __name__=="__main__":
    yinqiao_pill=Coldrex("Yinqiao Pill",2.0)
    penicillin=Antibiotic("Penicillin",3.0)
    doctor_prsrp=Prescription()
    doctor_prsrp.addMedicine(yinqiao_pill)
    doctor_prsrp.addMedicine(penicillin)
charger=Charger()
charger.setName("Doctor Strange")
pharmacy=Pharmacy()
pharmacy.setName("Doctor Wei")
doctor_prsrp.visit(charger)
doctor_prsrp.visit(pharmacy)

打印如下:

CHARGE: Doctor Strange lists the Medicine Yinqiao Pill. Price:2.0
CHARGE: Doctor Strange lists the Medicine Penicillin. Price:3.0
PHARMACY:Doctor Wei offers the Medicine Yinqiao Pill. Price:2.0
PHARMACY:Doctor Wei offers the Medicine Penicillin. Price:3.0

二、訪問者模式

訪問者模式的定義如下:封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作,它可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義于作用于這些元素的新操作。

訪問者模式

提到訪問者模式,就不得不提一下雙分派。分派分為靜態(tài)分派和動(dòng)態(tài)分派。首先解釋下靜態(tài)分派,靜態(tài)分派即根據(jù)請(qǐng)求者的名稱和接收到的參數(shù),決定多態(tài)時(shí)處理的操作。比如在Java或者C++中,定義名稱相同但參數(shù)不同的函數(shù)時(shí),會(huì)根據(jù)最終輸入的參數(shù)來決定調(diào)用哪個(gè)函數(shù)。雙分派顧名思義,即最終的操作決定于兩個(gè)接收者的類型,在本例中,藥品和工作人員互相調(diào)用了對(duì)方(藥品的accept和工作人員的visit中,對(duì)方都是參數(shù)),就是雙分派的一種應(yīng)用。
那么Python支持靜態(tài)分派么?先看下面的一個(gè)例子。

def max_num(x,y,z):
    return max(max(x,y),z)
def max_num(x,y):
    return max(x,y)
if __name__=="__main__":
    print max_num(1,2,4)

打印如下:

Traceback (most recent call last):
File "D:/WorkSpace/Project/PyDesignMode/example.py", line 786, in

TypeError: max_num() takes exactly 2 arguments (3 given)

可見,Python原生是不支持靜態(tài)分派的,因而也不直接支持更高層次的分派。訪問者模式實(shí)現(xiàn)的分派,是一種動(dòng)態(tài)雙分派。但這并不妨礙Python通過訪問者模式實(shí)現(xiàn)一種基于類的“雙分派效果”。Python多分派可以參考David Mertz 博士的一篇文章:可愛的Python:多分派—用多元法泛化多樣性。

三、訪問者模式的優(yōu)點(diǎn)和應(yīng)用場景

優(yōu)點(diǎn):

1、將不同的職責(zé)非常明確地分離開來,符合單一職責(zé)原則;
2、職責(zé)的分開也直接導(dǎo)致擴(kuò)展非常優(yōu)良,靈活性非常高,加減元素和訪問者都非常容易。

應(yīng)用場景:

1、要遍歷不同的對(duì)象,根據(jù)對(duì)象進(jìn)行不同的操作的場景;或者一個(gè)對(duì)象被多個(gè)不同對(duì)象順次處理的情況,可以考慮使用訪問者模式。除本例外,報(bào)表生成器也可以使用訪問者模式實(shí)現(xiàn),報(bào)表的數(shù)據(jù)源由多個(gè)不同的對(duì)象提供,每個(gè)對(duì)象都是Visitor,報(bào)表這個(gè)Element順次Accept各訪問者完善并生成對(duì)象。

四、訪問者模式的缺點(diǎn)

1、訪問者得知了元素細(xì)節(jié),與最小隔離原則相悖;
2、元素變更依舊可能引起Visitor的修改。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容