[Python設計模式] 第19章 分公司=部門?——組合模式

github地址:https://github.com/cheesezh/python_design_patternspython

組合模式

組合模式,將對象組合成樹形結構以表示「部分-總體」的層次結構。組合模式使得用戶對單個對象和對組合對象的使用具備一致性[DP]。git

from abc import ABCMeta, abstractmethod


class Component():
    """
    Component爲組合中的對象聲明接口,在適當狀況下,實現全部類共有接口的默認行爲。
    聲明一個接口用於訪問和管理Component的子部件。
    """
    __metaclass__ = ABCMeta
    
    def __init__(self, name):
        self.name = name
        
    @abstractmethod
    def add(self, c):
        """
        一般用add來增長樹枝或樹葉
        """
        pass
    
    @abstractmethod
    def remove(self, c):
        """
        一般用remove來刪除樹枝或樹葉
        """
        pass
    
    @abstractmethod
    def display(self, depth):
        pass
    
    
class Leaf(Component):
    """
    葉子節點
    """
    def add(self, c):
        print("葉子節點沒法添加子節點")
        
    def remove(self, c):
        print("葉子節點沒法刪除子節點")
        
    def display(self, depth):
        print("-"*depth, self.name)
        

class Composite(Component):
    """
    子部件節點
    """
    def __init__(self, name):
        super().__init__(name)
        self.children = []
        
    def add(self, c):
        self.children.append(c)
        
    def remove(self, c):
        self.children.remove(c)
        
    def display(self, depth):
        print("-"*depth, self.name)
        for c in self.children:
            c.display(depth+2)
            
            
def main():
    root = Composite("root")
    root.add(Leaf("Leaf A"))
    root.add(Leaf("Leaf B"))
    
    comp = Composite("Composite X")
    comp.add(Leaf("Leaf XA"))
    comp.add(Leaf("Leaf XB"))
    
    root.add(comp)
    
    comp2 = Composite("Composite XY")
    comp2.add(Leaf("Leaf XYA"))
    comp2.add(Leaf("Leaf XYB"))
    
    comp.add(comp2)
    
    root.add(Leaf("Leaf C"))
    
    leaf_d = Leaf("Leaf D")
    root.add(leaf_d)
    root.remove(leaf_d)
    
    root.display(1)
    
main()
- root
--- Leaf A
--- Leaf B
--- Composite X
----- Leaf XA
----- Leaf XB
----- Composite XY
------- Leaf XYA
------- Leaf XYB
--- Leaf C

透明方式與安全方式

Leaf類中也有Add和Reomve,可是樹葉不能夠再長分枝。這種方式叫作透明方式,也就是說再Component中聲明全部用來管理子對象的方法,其中包括add,remove等。這樣Component抽象類的全部子類都具有了add和remove。這樣的好處在於葉子節點和分枝節點對於外界沒有區別,它們具有徹底一致的行爲接口。可是問題也比較明顯,由於Leaf類自己不具有add和remove等功能,因此實現它是沒有意義的。github

另外一種是安全方式,也就是在Component接口中不去聲明add和remove方法,那麼子類Leaf也就不須要去實現它,而是在Composite聲明全部用來管理子類對象的方法,這樣作就不會出現剛纔提到的問題,不過因爲不透明,因此樹葉和樹枝類將有不一樣的接口,客戶端調用須要作相應的判斷,帶來了不便。安全

什麼時候使用組合模式

當需求中是體現部分與總體層次的結構時,以及但願用戶能夠忽略組合對象與單個對象的不一樣,統一地使用組合結構中的全部對象時,就應該考慮用組合模式了。架構

題目

使用組合模式,模擬公司管理系統。app

from abc import ABCMeta, abstractmethod


class Company():
    """
    抽象公司類
    """
    __metaclass__ = ABCMeta
    
    def __init__(self, name):
        self.name = name
        
    @abstractmethod
    def add(self, c):
        """
        一般用add來增長樹枝或樹葉
        """
        pass
    
    @abstractmethod
    def remove(self, c):
        """
        一般用remove來刪除樹枝或樹葉
        """
        pass
    
    @abstractmethod
    def display(self, depth):
        pass
    
    @abstractmethod
    def line_of_duty(self):
        pass
    

class ConcreteCompany(Company):
    """
    具體公司類
    """
    def __init__(self, name):
        super().__init__(name)
        self.children = []
        
    def add(self, c):
        self.children.append(c)
        
    def remove(self, c):
        self.children.remove(c)
        
    def display(self, depth):
        print("-"*depth, self.name)
        for c in self.children:
            c.display(depth+2)
            
    def line_of_duty(self):
        for c in self.children:
            c.line_of_duty()
            

class HRDepartment(Company):
    """
    人力資源部
    """ 
    def add(self, c):
        pass
        
    def remove(self, c):
        pass
        
    def display(self, depth):
        print("-"*depth, self.name)
            
    def line_of_duty(self):
        print("{}負責員工招聘。".format(self.name))
        
        
class FinanceDepartment(Company):
    """
    財務部
    """ 
    def add(self, c):
        pass
        
    def remove(self, c):
        pass
        
    def display(self, depth):
        print("-"*depth, self.name)
            
    def line_of_duty(self):
        print("{}負責財務收支。".format(self.name))
            
            
def main():
    root = ConcreteCompany("北京總公司")
    root.add(HRDepartment("總公司人力資源部"))
    root.add(FinanceDepartment("總公司財務部"))
    
    comp = ConcreteCompany("上海華東分公司")
    comp.add(HRDepartment("華東分公司人力資源部"))
    comp.add(FinanceDepartment("華東分公司財務部"))
    root.add(comp)
    
    comp1 = ConcreteCompany("南京辦事處")
    comp1.add(HRDepartment("南京辦事處人力資源部"))
    comp1.add(FinanceDepartment("南京辦事處財務部"))
    comp.add(comp1)
    
    comp2 = ConcreteCompany("杭州辦事處")
    comp2.add(HRDepartment("杭州辦事處人力資源部"))
    comp2.add(FinanceDepartment("杭州辦事處財務部"))
    comp.add(comp2)
    
    print("組織架構圖")
    root.display(1)
    
    print("履行職責")
    root.line_of_duty()
    
main()
組織架構圖
- 北京總公司
--- 總公司人力資源部
--- 總公司財務部
--- 上海華東分公司
----- 華東分公司人力資源部
----- 華東分公司財務部
----- 南京辦事處
------- 南京辦事處人力資源部
------- 南京辦事處財務部
----- 杭州辦事處
------- 杭州辦事處人力資源部
------- 杭州辦事處財務部
履行職責
總公司人力資源部負責員工招聘。
總公司財務部負責財務收支。
華東分公司人力資源部負責員工招聘。
華東分公司財務部負責財務收支。
南京辦事處人力資源部負責員工招聘。
南京辦事處財務部負責財務收支。
杭州辦事處人力資源部負責員工招聘。
杭州辦事處財務部負責財務收支。

點評

組合模式定義了包含人力資源部和財務部這些基本對象和分公司,辦事處等組合對象的類層次結構。基本對象能夠被組合成共複雜的組合對象,而這個組合對象又能夠被組合,這樣不斷地遞歸下去,客戶端代碼中,任何用到基本對象的地方均可以使用組合對象了。用戶不用關心究竟是處理一個葉子節點仍是處理一個組合組件,也用不着爲定義組合而寫一些選擇判斷語句。code

簡單的說,組合模式讓客戶能夠一致地使用組合結構和單個對象。orm

相關文章
相關標籤/搜索