第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()
**結構圖**
- 北京總公司
--- 總公司人力資源部
--- 總公司財務部
--- 上海華東分公司
----- 華東分公司人力資源部
----- 華東分公司財務部
----- 南京辦事處
------- 南京辦事處人力資源部
------- 南京辦事處財務部
----- 杭州辦事處
------- 杭州辦事處人力資源部
------- 杭州辦事處財務部
**職責**
總公司人力資源部 員工招聘培訓管理
總公司財務部 公司財務收支管理
華東分公司人力資源部 員工招聘培訓管理
華東分公司財務部 公司財務收支管理
南京辦事處人力資源部 員工招聘培訓管理
南京辦事處財務部 公司財務收支管理
杭州辦事處人力資源部 員工招聘培訓管理
杭州辦事處財務部 公司財務收支管理