組合模式 (Composite Pattern):將對象組合成樹形結構以表示“部分整體”的層次結構宙暇。組合模式使得用戶對單個對象和組合對象的使用具有一致性庶灿。組合模式也叫合成模式,有時候又叫做部分-整體模式。
(一)為什么需要組合模式
1,使我們在樹型結構的問題中,模糊了簡單元素和復雜元素的概念存筏,客戶程序可以像處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。
2味榛,組合模式讓你可以優(yōu)化處理遞歸或分級數據結構椭坚。
(二)組合模式UML圖
Component是組合中的對象聲明接口,在適當的情況下搏色,實現所有類共有接口的默認行為善茎。聲明一個接口用于訪問和管理Component子部件。
Leaf 在組合中表示葉子結點對象频轿,葉子結點沒有子結點垂涯。
Composite定義有枝節(jié)點行為,用來存儲子部件航邢,在Component接口中實現與子部件有關操作耕赘,如增加(add)和刪除(remove)等。
(三)簡單實例
如果我們在做一個OA系統(tǒng)膳殷,公司的人事管理該如何設計呢操骡。傳統(tǒng)的就是樹狀結構。經理下面有部門主管,然后是員工册招。
<?php
class Manager{
public $name;
protected $c_nodes = array();//存放子節(jié)點岔激,部門經理,普通員工等
public function __construct($name){
$this->name = $name;
}
//添加部門經理
public function addGm(GM $gm){
$this->c_nodes[] = $gm;
}
//添加普通員工
public function addStaff(Staff $staff){
$this->c_nodes[] = $staff;
}
//獲取全部子節(jié)點
public function get_C_nodes(){
return $this->c_nodes;
}
}
//部門經理 就用general manager 簡寫 GM
Interface Gm{
public function add(Staff $staff);
public function get_c_nodes();
}
//銷售經理
class Sgm implements Gm{
public $name;
protected $c_nodes = array();
public function __construct($name){
$this->name = $name;
}
//添加員工
public function add(Staff $staff){
$this->c_nodes = $staff;
}
//獲取子節(jié)點
public function get_C_nodes(){
return $this->c_nodes;
}
//區(qū)別于其他經理是掰,銷售經理有一個銷售方法
public function sell(){
echo "安利一下我司的產品";
}
}
//員工接口
Interface staff{
public function work();
}
//銷售部員工
class Sstaff implements staff{
public $name;
public function work(){
echo '在銷售經理帶領下虑鼎,安利全世界';
}
}
//實例化
$manager = new Manager("總經理");
$sgm = new Sgm("銷售經理");
$staff = new Sstaff("何在");
//組裝成樹
$manager->addGm($sgm);
$sgm->add($staff);
我們想象一下,如果我們的層級非常深键痛,如銷售經理下面還有炫彩,銷售主管,分區(qū)經理散休,分區(qū)主管媒楼。那怎么辦?我們new的時候戚丸,就要new很多不同的類。而且如果要加一個任職期限的屬性扔嵌,還得每個類去添加一遍限府。
我們想做的是,可以把樹形結構痢缎,當成“部分-整體結構”來處理胁勺,通俗地講,就是把一個樹形結構當成一個關系型的結構独旷。例如:數據庫存儲的一行行的方式署穗。找了張圖,清楚些嵌洼。
上面這張圖把中國 -湖南-(長沙)(衡陽)-這個樹狀圖以關系型的層級方式存儲于數據庫中案疲。對于公司人事,其實我們也是可以用這種方法的麻养。那就是利用組合模式褐啡。再去看一眼UML圖把和人事管理圖吧。我們發(fā)現總經理和部門經理還是有很多相同的地方鳖昌。組合模式主要就是把根節(jié)點和所有樹枝節(jié)點歸結到一起去备畦,這樣就隱藏了樹形的層級。
<?php
//抽象構件
Abstract class Component{
public $name;
abstract function doSomething();
public function __construct($name){
$this->name = $name;
}
}
//普通員工 樹葉構件 不能添加子節(jié)點
class Leaf extends Component{
public $lever;
public function doSomething(){
echo "層級--{$this->lever}--work";
}
}
//總經理 部門經理 主管等 樹枝構件
class Composite extends Component{
public $c_nodes = array();
public $lever = 1;
//添加子節(jié)點
public function add(Component $component){
$component->lever = $this->lever + 1;
$this->c_nodes[] = $component;
}
public function doSomething(){
echo "我是層級--{$this->lever}--".PHP_EOL;
}
}
$manager = new Composite("總經理");
$sgm = new Composite("銷售經理");
$staff = new Leaf("何在");
//組裝成樹
$manager->add($sgm);
$sgm->add($staff);
這樣许昨,我們就把根節(jié)點(總經理)和所有樹枝節(jié)點(部門經理懂盐,主管)的樹枝結構隱藏了,通過$lever屬性來區(qū)分糕档。如果對于不同樹枝節(jié)點有不同的方法莉恼,我們也可以在Composite 類中的doSomething()方法中延遲綁定具體的方法實現,使不同層級具有不同能力
public function doSomething(){
switch($this->lever){
case 1:$this->manager();
break;
case 2:$this->sell();
}
}
private function manager(){}
private function sell(){}
當然,當層級太深時类垫,若有多個層級有相同的doSomething能力司光。這種方法還是可以的。但是當層級太深且不同層級具有不同的doSomething能力時悉患,就會導致一個類中空置了多個不用的private方法残家,而doSomething只調用一個。
$lever 的另一個功能就是便于遞歸遍歷出所有公司人員
//遍歷樹 - 函數
function display(Composite $composite){
$composite->doSomething();
foreach($composite->c_nodes as $c_node)
$c_node instanceof Leaf ? $c_node->doSomething() : display($c_node);
}
display($manager);
當然這種遍歷方法只能前序遍歷售躁,即從根節(jié)點總經理向下找坞淮,沒法從任何一個員工向上找出他的上級。如果陪捷,你想實現后序遍歷回窘,可以在Component類中添加一個parent屬性,并在composite 的add方法中設置子節(jié)點的parent屬性市袖。
組合模式也分為透明模式和安全模式啡直,上面的例子是安全模式。透明模式是把composite的方法也放到抽象類component中苍碟。
有許多關于分級數據結構的例子酒觅,使得組合模式非常有用武之地。關于分級數據結構的一個普遍性的例子是你每次使用電腦時所遇到的:文件系統(tǒng)微峰。文件系統(tǒng)由目錄和文件組成舷丹。每個目錄都可以裝內容。目錄的內容可以是文件蜓肆,也可以是目錄颜凯。按照這種方式,計算機的文件系統(tǒng)就是以遞歸結構來組織的仗扬。如果你想要描述這樣的數據結構症概,那么你可以使用組合模式Composite。