[Python設計模式] 第28章 男人和女人——訪問者模式

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四人中的一個做者說過,大多數時候並不須要訪問者模式,但當一旦須要訪問者模式時,那就是真的須要它了。由於咱們很難找到數據結構不變化的狀況,因此用訪問者模式的機會也就不太多。

相關文章
相關標籤/搜索