PHP設(shè)計(jì)模式之組合模式互聯(lián)網(wǎng)公司流行扁平化管理,也就是管理層級(jí)盡量少于或者不超過(guò)三層,作為一個(gè)底層的碼農(nóng),你的CEO和你的職級(jí)也就相差3層以內(nèi)。但是很多傳統(tǒng)企業(yè),則會(huì)有非常深的層級(jí)關(guān)系,從數(shù)據(jù)結(jié)構(gòu)看,這種按職能進(jìn)行分組的組織架構(gòu)非常像一顆樹(shù)。而我們今天介紹的組合模式的作用就和這個(gè)企業(yè)組織架構(gòu)層級(jí)的模式非常類似。 Gof類圖及解釋
GoF定義:將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。Composite使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性 GoF類圖
 代碼實(shí)現(xiàn)
abstract class Component { protected $name;
public function __construct($name){ $this->name = $name; } abstract public function Operation(int $depth);
abstract public function Add(Component $component);
abstract public function Remove(Component $component); }
抽象出來(lái)的組合節(jié)點(diǎn)聲明,在適當(dāng)情況下實(shí)現(xiàn)所有類的公共接口的缺省行為,是所有子節(jié)點(diǎn)的父類。 class Composite extends Component { private $componentList;
public function Operation($depth) { echo str_repeat('-', $depth) . $this->name . PHP_EOL; foreach ($this->componentList as $component) { $component->Operation($depth + 2); } }
public function Add(Component $component) { $this->componentList[] = $component; }
public function Remove(Component $component) { $position = 0; foreach ($this->componentList as $child) { ++$position; if ($child == $component) { array_splice($this->componentList, ($position), 1); } } }
public function GetChild(int $i) { return $this->componentList[$i]; } }
具體的節(jié)點(diǎn)實(shí)現(xiàn)類,保存下級(jí)節(jié)點(diǎn)的引用,定義實(shí)際的節(jié)點(diǎn)行為。 class Leaf extends Component { public function Add(Component $c) { echo 'Cannot add to a leaf' . PHP_EOL; } public function Remove(Component $c) { echo 'Cannot remove from a leaf' . PHP_EOL; } public function Operation(int $depth) { echo str_repeat('-', $depth) . $this->name . PHP_EOL; } }
葉子節(jié)點(diǎn),沒(méi)有子節(jié)點(diǎn)的最終節(jié)點(diǎn)。 從來(lái)代碼來(lái)看,完全就是一顆樹(shù)的實(shí)現(xiàn) 所有的子節(jié)點(diǎn)和葉子節(jié)點(diǎn)都可以處理數(shù)據(jù),但葉子節(jié)點(diǎn)為終點(diǎn) 你希望用戶可以忽略組合對(duì)象與單個(gè)對(duì)象的不同,統(tǒng)一地使用組合結(jié)構(gòu)中的所有對(duì)象時(shí),就應(yīng)該考慮使用組合模式 用戶不用關(guān)心到底是處理一個(gè)葉節(jié)點(diǎn)還是處理一個(gè)組合組件 ,也就用不著為定義組合而寫一些選擇判斷語(yǔ)句了 組合模式可以讓客戶一致性地使用組合結(jié)構(gòu)和單個(gè)對(duì)象
接著文章最開(kāi)頭的例子來(lái)說(shuō),在我們的組織架構(gòu)中,一項(xiàng)任務(wù)下達(dá)到最底的人員時(shí),會(huì)經(jīng)歷多個(gè)層級(jí)。我還是比較喜歡傳統(tǒng)一起的企業(yè)管理方式。通常是一名總監(jiān)對(duì)應(yīng)多個(gè)主管,一名主管對(duì)應(yīng)多位經(jīng)理,一位經(jīng)理對(duì)應(yīng)多位組長(zhǎng),一名組長(zhǎng)對(duì)應(yīng)多名員工。當(dāng)一個(gè)通知下發(fā)時(shí),每一層級(jí)的工作人員都要做出回應(yīng),并將通知繼續(xù)下發(fā)到下屬員工那里,同時(shí)從下屬哪里獲得反饋。這樣,我們就不知不覺(jué)地在實(shí)踐中完成了一次組合模式的應(yīng)用。突然感覺(jué)自己棒棒噠,感覺(jué)人生已經(jīng)到達(dá)了巔峰?。?/em> 完整代碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/14.composite/source/composite.php 實(shí)例短信短信,這個(gè)功能我們可以是翻來(lái)覆去的用了。這次也不例外。這一回我們的網(wǎng)站后臺(tái)的功能是要針對(duì)不同分站和不同來(lái)源的用戶進(jìn)行短信的發(fā)送。在這里,我們依然只關(guān)注短信發(fā)送這件事兒,我們希望給你不同渠道角色但包含統(tǒng)一行為的用戶,你來(lái)進(jìn)行發(fā)送就行了,這樣的功能似乎并不難吧! 短信發(fā)送類圖
 完整源碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/14.composite/source/composite-msg.php <?php
abstract class Role { protected $userRoleList; protected $name; public function __construct(String $name) { $this->name = $name; }
abstract public function Add(Role $role);
abstract public function Remove(Role $role);
abstract public function SendMessage(); }
class RoleManger extends Role { public function Add(Role $role) { $this->userRoleList[] = $role; }
public function Remove(Role $role) { $position = 0; foreach ($this->userRoleList as $n) { ++$position; if ($n == $role) { array_splice($this->userRoleList, ($position), 1); } } }
public function SendMessage() { echo "開(kāi)始發(fā)送用戶角色:" . $this->name . '下的所有用戶短信', PHP_EOL; foreach ($this->userRoleList as $role) { $role->SendMessage(); } } }
class Team extends Role {
public function Add(Role $role) { echo "小組用戶不能添加下級(jí)了!", PHP_EOL; }
public function Remove(Role $role) { echo "小組用戶沒(méi)有下級(jí)可以刪除!", PHP_EOL; }
public function SendMessage() { echo "小組用戶角色:" . $this->name . '的短信已發(fā)送!', PHP_EOL; } }
// root用戶 $root = new RoleManger('網(wǎng)站用戶'); $root->add(new Team('主站用戶')); $root->SendMessage();
// 社交版塊 $root2 = new RoleManger('社交版塊'); $managerA = new RoleManger('論壇用戶'); $managerA->add(new Team('北京論壇用戶')); $managerA->add(new Team('上海論壇用戶'));
$managerB = new RoleManger('sns用戶'); $managerB->add(new Team('北京sns用戶')); $managerB->add(new Team('上海sns用戶'));
$root2->add($managerA); $root2->add($managerB); $root2->SendMessage();
說(shuō)明
當(dāng)我要發(fā)送論壇版塊的用戶時(shí),我可以自由地添加各地方站的葉子節(jié)點(diǎn)來(lái)控制發(fā)送對(duì)象 你可以把整個(gè)$root2的發(fā)送看作是一個(gè)整體,不同的版塊和地區(qū)看成是部分 這個(gè)組合可以一直向下延伸,直到深度的葉子節(jié)點(diǎn)結(jié)束,這個(gè)度當(dāng)然是由自己來(lái)把控,很清晰明了
下期看點(diǎn)組合模式最大的特點(diǎn)就是可以讓葉子節(jié)點(diǎn)或者子節(jié)點(diǎn)無(wú)限的組合和延伸。能夠形成各種不同的組合方式,但又能保證萬(wàn)變不離其宗。讓整個(gè)遞歸是在可控的范圍內(nèi)進(jìn)行,很牛X吧!!下一篇我們將學(xué)習(xí)到的是中介者模式,它和我們經(jīng)常打交道的房產(chǎn)中介有什么區(qū)別呢?別急,下次再聊!
|