第19章:组合模式
组合模式
组合模式(Composite):将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式
适合处理整体与部分可以被一致对待的问题。
Component
为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component
的子部件。
Leaf
在组合中表示叶节点对象,叶节点没有子节点。
Composite
定义有枝节点行为,用来存储子部件,在Component
接口中实现与子部件有关的操作,比如增加Add
和删除Remove
。
客户端代码,能通过Component
接口操作组合部件的对象。
结果显示
透明方式与安全方式
透明方式
:Leaf
类不可以再长分枝,但Leaf
类也有Add
和Remove
方法,即在Component
类中声明所有用来管理子对象的方法,这样实现Component
接口的所有子类都具备了Add
和Remove
等方法。这样做的好处是叶节点
和枝节点
对于外界没有区别,它们具备完全一致的行为接口。但问题是,Leaf
类本身不具备Add
和Remove
方法的功能,所以实现它没有意义。
安全方式
:在Component
接口中不声明Add
和Remove
方法,则子类Leaf
无需实现Add
和Remove
方法,在子类Composite
中声明所有用来管理子类对象的方法。不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断。
组合模式的使用
当需求是体现部分与整体层次的结构时,并且用户希望忽略组合对象与单个对象的不同、统一地使用组合结构中的所有对象,应该使用组合模式。
例:ASP.NET
的TreeView
控件、自定义控件(把一些基本的控件组合起来,通过编程写成个定制的控件,比如用两个文本框和一个按钮可以定义的登录框控件)。
所有的Web控件
的基类都是System.Web.UI.Control
,而Control
基类中就有Add
和Remove
方法,这就是典型的组合模式的应用。
组合模式的好处
-
组合模式定义了包含基本对象和组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象。
-
用户不用关心到底是处理一个叶节点还是处理一个组合组件,也就不用为定义组合而写一些选择判断语句,即,组合模式让客户可以一致地使用组合结构和单个对象。
组合模式示例
任务:公司管理系统
from abc import ABC, abstractmethod
from typing import Text
class Company(ABC):
"""
公司类(抽象类或接口)
"""
def __init__(self, name: Text):
self._name = name
@abstractmethod
def add(self, c: object) -> None:
"""
增加
"""
pass
@abstractmethod
def remove(self, c: object) -> None:
"""
移除
"""
pass
@abstractmethod
def display(self, depth: int) -> None:
"""
显示
"""
pass
@abstractmethod
def line_of_duty(self) -> None:
"""
履行职责
"""
pass
class ConcreteCompany(Company):
"""
具体公司类(实现接口树枝节点)
"""
def __init__(self, name: Text):
super(ConcreteCompany, self).__init__(name)
self.__children = []
def add(self, c: Company) -> None:
self.__children.append(c)
def remove(self, c: Company) -> None:
self.__children.remove(c)
def display(self, depth: int) -> None:
print("-" * depth, self._name)
for component in self.__children:
component.display(depth + 2)
def line_of_duty(self) -> None:
for component in self.__children:
component.line_of_duty()
class HRDepartment(Company):
"""
人力资源部与财务部类树叶节点
"""
def __init__(self, name: Text):
super(HRDepartment, self).__init__(name)
def add(self, c: Company) -> None:
pass
def remove(self, c: Company) -> None:
pass
def display(self, depth: int) -> None:
print("-" * depth, self._name)
def line_of_duty(self) -> None:
print(self._name, "员工招聘培训管理")
class FinanceDepartment(Company):
"""
人力资源部与财务部类树叶节点
"""
def __init__(self, name: Text):
super(FinanceDepartment, self).__init__(name)
def add(self, c: Company) -> None:
pass
def remove(self, c: Company) -> None:
pass
def display(self, depth: int) -> None:
print("-" * depth, self._name)
def line_of_duty(self) -> None:
print(self._name, "公司财务收支管理")
# 客户端调用
if __name__ == "__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()
**结构图**
- 北京总公司
--- 总公司人力资源部
--- 总公司财务部
--- 上海华东分公司
----- 华东分公司人力资源部
----- 华东分公司财务部
----- 南京办事处
------- 南京办事处人力资源部
------- 南京办事处财务部
----- 杭州办事处
------- 杭州办事处人力资源部
------- 杭州办事处财务部
**职责**
总公司人力资源部 员工招聘培训管理
总公司财务部 公司财务收支管理
华东分公司人力资源部 员工招聘培训管理
华东分公司财务部 公司财务收支管理
南京办事处人力资源部 员工招聘培训管理
南京办事处财务部 公司财务收支管理
杭州办事处人力资源部 员工招聘培训管理
杭州办事处财务部 公司财务收支管理