[Python设计模式] 第19章 分公司=部门?——组合模式

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

组合模式

组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和对组合对象的使用具有一致性[DP]。

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等功能,所以实现它是没有意义的。

另一种是安全方式,也就是在Component接口中不去声明add和remove方法,那么子类Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题,不过由于不透明,所以树叶和树枝类将有不同的接口,客户端调用需要做相应的判断,带来了不便。

何时使用组合模式

当需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式了。

题目

使用组合模式,模拟公司管理系统。

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()
    
组织架构图
- 北京总公司
--- 总公司人力资源部
--- 总公司财务部
--- 上海华东分公司
----- 华东分公司人力资源部
----- 华东分公司财务部
----- 南京办事处
------- 南京办事处人力资源部
------- 南京办事处财务部
----- 杭州办事处
------- 杭州办事处人力资源部
------- 杭州办事处财务部
履行职责
总公司人力资源部负责员工招聘。
总公司财务部负责财务收支。
华东分公司人力资源部负责员工招聘。
华东分公司财务部负责财务收支。
南京办事处人力资源部负责员工招聘。
南京办事处财务部负责财务收支。
杭州办事处人力资源部负责员工招聘。
杭州办事处财务部负责财务收支。

点评

组合模式定义了包含人力资源部和财务部这些基本对象和分公司,办事处等组合对象的类层次结构。基本对象可以被组合成共复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户端代码中,任何用到基本对象的地方都可以使用组合对象了。用户不用关心到底是处理一个叶子节点还是处理一个组合组件,也用不着为定义组合而写一些选择判断语句。

简单的说,组合模式让客户可以一致地使用组合结构和单个对象。

原文地址:https://www.cnblogs.com/CheeseZH/p/9462244.html