github地址:https://github.com/cheesezh/python_design_patternspython
用程序模擬如下不一樣狀況:git
from abc import ABCMeta,abstractmethod class Person(): __metaclass__ = ABCMeta def __init__(self): self.action = None @abstractmethod def get_conclusion(self): pass class Man(Person): def get_conclusion(self): if self.action == "成功": print("男人成功時,背後多半有一個偉大的女人") elif self.action == "失敗": print("男人失敗時,悶頭喝酒,誰也不用勸") elif self.action == "戀愛": print("男人戀愛時,凡事不懂也要裝逼") class Woman(Person): def get_conclusion(self): if self.action == "成功": print("女人成功時,背後多半有一個失敗的男人") elif self.action == "失敗": print("女人失敗時,眼淚汪汪,誰也勸不了") elif self.action == "戀愛": print("女人戀愛時,遇事懂也裝做不懂") def main(): persons = [] man1 = Man() man1.action = "成功" persons.append(man1) woman1 = Woman() woman1.action = "成功" persons.append(woman1) man2 = Man() man2.action = "失敗" persons.append(man2) woman2 = Woman() woman2.action = "失敗" persons.append(woman2) man3 = Man() man3.action = "戀愛" persons.append(man3) woman3 = Woman() woman3.action = "戀愛" persons.append(woman3) for p in persons: p.get_conclusion() main()
男人成功時,背後多半有一個偉大的女人 女人成功時,背後多半有一個失敗的男人 男人失敗時,悶頭喝酒,誰也不用勸 女人失敗時,眼淚汪汪,誰也勸不了 男人戀愛時,凡事不懂也要裝逼 女人戀愛時,遇事懂也裝做不懂
上述代碼算是初步的面向對象編程,可是在「Man」和「Woman」中那些「if-else」判斷很複雜,若是再增長「結婚」狀態,那須要增長分支判斷。github
這裏咱們可使用訪問者模式。算法
訪問者模式,表示一個做用於某對象結構中各元素的操做。它使你能夠在不改變各元素的類的前提下,定義做用於這些元素的新操做。[DP]編程
訪問者模式的基本代碼以下:數據結構
from abc import ABCMeta, abstractmethod class Visitor(): """ Visitor類,爲該對象結構中ConcretElement的每個類聲明一個Visit操做。 """ __metaclass__ = ABCMeta @abstractmethod def visit_concret_element_a(self, concret_element_a): pass @abstractmethod def visit_concret_element_b(self, concret_element_b): pass class ConcretVisitor1(Visitor): """ ConcretVisitor類,具體訪問者,實現每一個由Visitor聲明的操做。每一個操做實現算法的一部分, 而該算法片斷乃是對應於結構中對象的類。 """ def visit_concret_element_a(self, concret_element_a): print("Visitor1 visit element A") def visit_concret_element_b(self, concret_element_b): print("Visitor1 visit element B") class ConcretVisitor2(Visitor): """ 同上 """ def visit_concret_element_a(self, concret_element_a): print("Visitor2 visit element A") def visit_concret_element_b(self, concret_element_b): print("Visitor2 visit element B") class Element(): """ Element類,定義一個Accept操做,它以一個訪問者爲參數 """ __metaclass__ = ABCMeta @abstractmethod def accept(self, visitor): pass class ConcretElementA(Element): """ 具體元素類,實現Accept操做 """ def accept(self, visitor): visitor.visit_concret_element_a(self) def other_functions_a(self): pass class ConcretElementB(Element): """ 具體元素類,實現Accept操做 """ def accept(self, visitor): visitor.visit_concret_element_b(self) def other_functions_b(self): pass class ObjectStructure(): """ ObjectStructure類,能枚舉他的元素,能夠提供一個高層的接口以容許訪問者訪問它的元素。 """ def __init__(self): self.elements = [] def accept(self, visitor): for element in self.elements: element.accept(visitor) def main(): o = ObjectStructure() o.elements.append(ConcretElementA()) o.elements.append(ConcretElementB()) v1 = ConcretVisitor1() v2 = ConcretVisitor2() o.accept(v1) o.accept(v2) main()
Visitor1 visit element A Visitor1 visit element B Visitor2 visit element A Visitor2 visit element B
from abc import ABCMeta, abstractmethod class Action(): """ 狀態抽象類,Visitor類,爲該對象結構中ConcretElement的每個類聲明一個Visit操做。 """ __metaclass__ = ABCMeta @abstractmethod def get_man_conclusion(self, man): pass @abstractmethod def get_woman_conclusion(self, woman): pass class Success(Action): """ 成功狀態類,ConcretVisitor類,具體訪問者,實現每一個由Visitor聲明的操做。每一個操做實現算法的一部分, 而該算法片斷乃是對應於結構中對象的類。 """ def get_man_conclusion(self, man): print("男人成功時,背後多半有一個偉大的女人") def get_woman_conclusion(self, woman): print("女人成功時,背後多半有一個失敗的男人") class Failing(Action): """ 失敗狀態類,ConcretVisitor類,具體訪問者,實現每一個由Visitor聲明的操做。每一個操做實現算法的一部分, 而該算法片斷乃是對應於結構中對象的類。 """ def get_man_conclusion(self, man): print("男人失敗時,悶頭喝酒,誰也不用勸") def get_woman_conclusion(self, woman): print("女人失敗時,眼淚汪汪,誰也勸不了") class Amativeness(Action): """ 戀愛狀態類,ConcretVisitor類,具體訪問者,實現每一個由Visitor聲明的操做。每一個操做實現算法的一部分, 而該算法片斷乃是對應於結構中對象的類。 """ def get_man_conclusion(self, man): print("男人戀愛時,凡事不懂也要裝逼") def get_woman_conclusion(self, woman): print("女人戀愛時,遇事懂也裝做不懂") class Person(): """ 人抽象類,Element類,定義一個Accept操做,它以一個訪問者爲參數 """ __metaclass__ = ABCMeta @abstractmethod def accept(self, visitor): pass class Man(Person): """ 男人類,具體元素類,實現Accept操做 """ def accept(self, visitor): """ 雙分派技術: 第一次分派:首先在客戶端程序中將具體狀態做爲參數傳遞給「男人」類完成一次分派; 第二次分派:而後「男人」類調用做爲參數的「具體狀態」中的方法「男人反應」,同事將本身(self)做爲參數傳遞進去; """ visitor.get_man_conclusion(self) def other_functions_a(self): pass class Woman(Person): """ 女人類,具體元素類,實現Accept操做 """ def accept(self, visitor): visitor.get_woman_conclusion(self) def other_functions_b(self): pass class ObjectStructure(): """ ObjectStructure類,能枚舉他的元素,能夠提供一個高層的接口以容許訪問者訪問它的元素。 """ def __init__(self): self.elements = [] def accept(self, visitor): for element in self.elements: element.accept(visitor) def main(): o = ObjectStructure() o.elements.append(Man()) o.elements.append(Woman()) v1 = Success() v2 = Failing() v3 = Amativeness() o.accept(v1) print("-"*10) o.accept(v2) print("-"*10) o.accept(v3) main() print() print("因爲使用了雙分派技術,當新增`結婚`狀態時,只須要新增一個狀態子類,而後修改客戶端便可。") print() class Marriage(Action): def get_man_conclusion(self, man): print("男人結婚時,有妻徒刑遙無期") def get_woman_conclusion(self, woman): print("女人結婚時,婚姻保險保平安") def main(): o = ObjectStructure() o.elements.append(Man()) o.elements.append(Woman()) v1 = Success() v2 = Failing() v3 = Amativeness() v4 = Marriage() o.accept(v1) print("-"*10) o.accept(v2) print("-"*10) o.accept(v3) print("-"*10) o.accept(v4) main()
男人成功時,背後多半有一個偉大的女人 女人成功時,背後多半有一個失敗的男人 ---------- 男人失敗時,悶頭喝酒,誰也不用勸 女人失敗時,眼淚汪汪,誰也勸不了 ---------- 男人戀愛時,凡事不懂也要裝逼 女人戀愛時,遇事懂也裝做不懂 因爲使用了雙分派技術,當新增`結婚`狀態時,只須要新增一個狀態子類,而後修改客戶端便可。 男人成功時,背後多半有一個偉大的女人 女人成功時,背後多半有一個失敗的男人 ---------- 男人失敗時,悶頭喝酒,誰也不用勸 女人失敗時,眼淚汪汪,誰也勸不了 ---------- 男人戀愛時,凡事不懂也要裝逼 女人戀愛時,遇事懂也裝做不懂 ---------- 男人結婚時,有妻徒刑遙無期 女人結婚時,婚姻保險保平安
訪問者模式適用於數據結構相對穩定的系統,它把數據結構和做用於數據結構上的操做之間的耦合解脫開,使得操做結合能夠相對自由地演化。app
訪問這模式的目的是要把處理
從數據結構
分離出來。不少系統能夠按照算法和數據結構分開,若是這樣的系統有比較穩定的數據結構,又有易於變化的算法的話,那就比較適合使用訪問者模式,由於訪問者模式使得算法操做的增長變得容易。反之,若是系統的數據結構對象易於變化,常常要有新的數據對象增長進來,就不適合使用訪問者模式。code
訪問者模式的優勢就是增長新的操做很容易,由於增長新的操做就意味着增長一個新的訪問者。訪問者模式將有關的行爲集中到一個訪問者對象中。對象
訪問者模式的缺點其實就是增長新的數據結構變得困難了。接口
GoF四人中的一個做者說過,大多數時候並不須要訪問者模式,但當一旦須要訪問者模式時,那就是真的須要它了。由於咱們很難找到數據結構不變化的狀況,因此用訪問者模式的機會也就不太多。