PHP設計模式(十)—組合模式(Composite Pattern)

組合模式 (Composite Pattern):將對象組合成樹形結構以表示“部分整體”的層次結構宙暇。組合模式使得用戶對單個對象和組合對象的使用具有一致性庶灿。組合模式也叫合成模式,有時候又叫做部分-整體模式。

(一)為什么需要組合模式

1,使我們在樹型結構的問題中,模糊了簡單元素和復雜元素的概念存筏,客戶程序可以像處理簡單元素一樣來處理復雜元素,從而使得客戶程序與復雜元素的內部結構解耦。

2味榛,組合模式讓你可以優(yōu)化處理遞歸或分級數據結構椭坚。

(二)組合模式UML圖

Composite Pattern
  • 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。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末厉颤,一起剝皮案震驚了整個濱河市穴豫,隨后出現的幾起案子,更是在濱河造成了極大的恐慌逼友,老刑警劉巖精肃,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異帜乞,居然都是意外死亡司抱,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門黎烈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來习柠,“玉大人匀谣,你說我怎么就攤上這事∽世#” “怎么了武翎?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵毕籽,是天一觀的道長枝誊。 經常有香客問我,道長憎账,這世上最難降的妖魔是什么趴捅? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任垫毙,我火速辦了婚禮,結果婚禮上拱绑,老公的妹妹穿的比我還像新娘综芥。我一直安慰自己,他們只是感情好猎拨,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布膀藐。 她就那樣靜靜地躺著,像睡著了一般迟几。 火紅的嫁衣襯著肌膚如雪消请。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天类腮,我揣著相機與錄音,去河邊找鬼蛉加。 笑死蚜枢,一個胖子當著我的面吹牛,可吹牛的內容都是我干的针饥。 我是一名探鬼主播厂抽,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丁眼!你這毒婦竟也來了筷凤?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤苞七,失蹤者是張志新(化名)和其女友劉穎藐守,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體蹂风,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡卢厂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了惠啄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慎恒。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡任内,死狀恐怖,靈堂內的尸體忽然破棺而出融柬,到底是詐尸還是另有隱情死嗦,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布粒氧,位于F島的核電站越除,受9級特大地震影響,放射性物質發(fā)生泄漏靠欢。R本人自食惡果不足惜廊敌,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望门怪。 院中可真熱鬧骡澈,春花似錦、人聲如沸掷空。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坦弟。三九已至护锤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酿傍,已是汗流浹背烙懦。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赤炒,地道東北人氯析。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像莺褒,于是被迫代替她去往敵國和親掩缓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容