設計模式 - Composite Pattern 組合模式
Intro
假設我們今天要建構一個可以代表公司與部門之間結構的關係,公司可以包含分公司、可以包含部門,而部門可以包含子部門或是員工。
實作
我們先建立一個Component類別,讓它具有可以擴充及移除的功能,且期望所有他的子類別都能夠有固定的行為(operation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| abstract class Component { protected $name;
public function __construct(string $name) { $this->name = $name; } abstract public function add(Component $component);
abstract public function remove(Component $component);
abstract public function operation(); }
|
再來我們新增兩個Component的子類別
Composite : 可以繼續擴充
Leaf : 不能擴充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| class Composite extends Component { protected $children;
public function __construct(string $name) { parent::__construct($name); }
public function add(Component $component) { $this->children[] = $component; }
public function remove(Component $component) { }
public function operation() { } }
class Leaf extends Component { public function __construct(string $name) { parent::__construct($name); }
public function add(Component $component) { throw new Exception('Leaf can not add'); }
public function remove(Component $component) { throw new Exception('Leaf can not remove'); }
public function operation() { } }
|
使用方式大概如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| $MainCompany = new Composite('總公司');
$MainCompanyDepartmentA = new Composite('總公司的部門A'); $MainCompanyDepartmentAEmployeeA = new Leaf('總公司的部門A-員工A'); $MainCompanyDepartmentA->add($MainCompanyDepartmentAEmployeeA); $MainCompany->add($MainCompanyDepartmentA);
$MainCompanyDepartmentB = new Composite('總公司的部門B'); $MainCompanyDepartmentBEmployeeA = new Leaf('總公司的部門B-員工A'); $MainCompanyDepartmentB->add($MainCompanyDepartmentBEmployeeA); $MainCompany->add($MainCompanyDepartmentB);
$SubCompanyA = new Composite('分公司A'); $SubCompanyADepartmentA = new Composite('分公司A的部門A'); $SubCompanyADepartmentAEmployeeA = new Leaf('分公司A的部門A-員工A'); $SubCompanyADepartmentA->add($SubCompanyADepartmentAEmployeeA); $SubCompanyA->add($SubCompanyADepartmentA); $MainCompany->add($SubCompanyA);
$SubCompanyB = new Composite('分公司B'); $SubCompanyBDepartmentA = new Composite('分公司B的部門A'); $SubCompanyBDepartmentAEmployeeA = new Leaf('分公司B的部門A-員工A'); $SubCompanyBDepartmentAEmployeeB = new Leaf('分公司B的部門A-員工B'); $SubCompanyBDepartmentA->add($SubCompanyBDepartmentAEmployeeA); $SubCompanyBDepartmentA->add($SubCompanyBDepartmentAEmployeeB); $SubCompanyB->add($SubCompanyBDepartmentA); $MainCompany->add($SubCompanyB);
$SubCompanyBDepartmentB = new Composite('分公司B的部門B'); $SubCompanyBDepartmentBEmployeeA = new Leaf('分公司B的部門B-員工A'); $SubCompanyBDepartmentB->add($SubCompanyBDepartmentBEmployeeA); $MainCompany->add($SubCompanyBDepartmentB);
|
透過這樣的過程可以建構出像下圖的結構
Plant UML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @startuml
node MainCompany node Main_DepartmentA actor Employee1 node Main_DepartmentB actor Employee2
node SubCompanyA node A_DepartmentA actor Employee3
node SubCompanyB node B_DepartmentA actor Employee4 actor Employee5 node B_DepartmentB actor Employee6
MainCompany --> SubCompanyA MainCompany --> SubCompanyB MainCompany --> Main_DepartmentA MainCompany --> Main_DepartmentB Main_DepartmentA --> Employee1 Main_DepartmentB --> Employee2 SubCompanyA --> A_DepartmentA A_DepartmentA --> Employee3 SubCompanyB --> B_DepartmentA SubCompanyB --> B_DepartmentB B_DepartmentA --> Employee4 B_DepartmentA --> Employee5 B_DepartmentB --> Employee6
@enduml
|
時機
- 希望對結構整體或部分甚至是單一物件都可以使用同樣地行為操作時
- 當你要時作的對象擁有的結構類似於”樹狀”時
目的
將物件組合成「樹狀」結構以表示「部分—全體」的層次結構。讓外界以一致性的方式對待個別物件和整體物件
類別圖
Plant UML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @startuml
skinparam classAttributeIconSize 0
class Component { {method} + add(Component $component) {method} + remove(Component $component) {method} + operation() }
class Composite { {method} + add(Component $component) {method} + remove(Component $component) {method} + operation() }
class Leaf { {method} + add(Component $component) {method} + remove(Component $component) {method} + operation() }
Leaf -up-|> Component Composite -up-|> Component Composite o-up-> Component : children
@enduml
|
優點
- 容易加入新地Component,擴充容易
- 對於使用者來說,不必在乎他使用的對象是Leaf或是Component,他們擁有相同地對外行為
缺點