设计模式 - 组合模式(Composite Pattern)

参考:http://terrylee.cnblogs.com/archive/2006/03/11/347919.html

简介

树状结构中,对于枝节点和叶节点,接口不同,客户端需要判断处理的节点类型。

场景

对于规模比较大的公司,其组织架构一般是:总公司,下面有分公司和直属部门(总公司的人事、财务、行政),分公司下面又有直属部门(分公司的人事、财务、行政)和支公司,支公司下面是各个部门。

在客户端程序中,需要判断返回对象的具体类型到底是公司还是部门。如果是公司,则需要递归处理。

这增加了客户端程序与复杂元素内部结构之间的依赖。组合模式可以解决这种问题。

模式定义

组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式模糊了树型结构问题中简单元素和复杂元素的概念,客户程序可以跟处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

模式特点

组合模式包含四个角色:

  • Component:对象声明接口,声明所有类共有的默认行为
  • Composite:有子节点的节点,存储子节点并实现 Component 接口有关操作
  • Leaf:叶节点,没有子节点,用于定义对象的基本行为
  • Client:客户类

组合模式
组合模式可以不提供父对象的管理方法,但必须提供子对象的管理方法(add、remove 等)。

根据所实现接口的区别,组合模式包括两种:

  • 透明组合模式:叶节点和枝节点接口统一。在 Component 中声明所有用来管理子对象的方法(Add、Remove 等)。这样实现 Component 接口的所有子类都具备了管理子对象的方法。问题很明显,因为 Leaf 类本身不具备管理子对象的功能,所以实现的部分方法没有意义。
  • 安全组合模式:叶节点和枝节点接口不统一。不在 Component 中声明用来管理子对象的方法,而是在 Composite 声明所有用来管理子类对象的方法。问题也很明显,叶节点和枝节点将不具有相同的接口,客户端需要进行判断。

优点

  • 将客户代码与复杂的对象容器结构解耦,客户代码只和抽象接口发生依赖关系
  • 透明组合模式的叶节点和枝节点有完全一致的行为接口

缺点

  • 透明组合模式的叶节点中,部分接口是没有意义的
  • 安全组合模式的叶节点和枝节点具有不同的接口,客户端调用需要做相应的判断

PHP 代码示例


<?php

abstract class Component {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public abstract function Add(Component $c);
    public abstract function Remove(Component $c);
    public abstract function Display(int $depth);
}

class Composite extends Component {
    private $children = array();

    public function Add(Component $c) {
        array_push($this->children, $c);
    }

    public function Remove(Component $c) {
        array_pop($this->children);
    }

    public function Display(int $depth) {
        echo '-' . $depth . '  ' . $this->name . "<br/>";

        foreach ($this->children as $component) {
            $component->Display($depth + 2);
        }
    }
}

class Leaf extends Component {
    public function Add(Component $c) {
        echo "Cannot add to a leaf" . "<br/>";
    }

    public function Remove(Component $c) {
        echo "Cannot remove from a leaf" . "<br/>";
    }

    public function Display(int $depth) {
        echo '-' . depth . $this->name . "<br/>";
    }
}

echo '<pre>';

$root = new Composite("root");
$root->Add(new Leaf("Leaf A"));
$root->Add(new Leaf("Leaf B"));

$comp = new Composite("Composite X");
$comp->Add(new Leaf("Leaf XA"));
$comp->Add(new Leaf("Leaf XB"));

$root->Add($comp);

$comp2 = new Composite("Composite XY");
$comp2->Add(new Leaf("Leaf XYA"));
$comp2->Add(new Leaf("Leaf XYB"));

$comp->Add($comp2);

$root->Add(new Leaf("Leaf C"));

print_r($root);

$leaf = new Leaf("Leaf D");
$root->Add($leaf);
$root->Remove($leaf);

$root->Display(1);

输出:

Composite Object
(
    [children:Composite:private] => Array
        (
            [0] => Leaf Object
                (
                    [name:protected] => Leaf A
                )

            [1] => Leaf Object
                (
                    [name:protected] => Leaf B
                )

            [2] => Composite Object
                (
                    [children:Composite:private] => Array
                        (
                            [0] => Leaf Object
                                (
                                    [name:protected] => Leaf XA
                                )

                            [1] => Leaf Object
                                (
                                    [name:protected] => Leaf XB
                                )

                            [2] => Composite Object
                                (
                                    [children:Composite:private] => Array
                                        (
                                            [0] => Leaf Object
                                                (
                                                    [name:protected] => Leaf XYA
                                                )

                                            [1] => Leaf Object
                                                (
                                                    [name:protected] => Leaf XYB
                                                )

                                        )

                                    [name:protected] => Composite XY
                                )

                        )

                    [name:protected] => Composite X
                )

            [3] => Leaf Object
                (
                    [name:protected] => Leaf C
                )

        )

    [name:protected] => root
)
-1  root
-depthLeaf A
-depthLeaf B
-3  Composite X
-depthLeaf XA
-depthLeaf XB
-5  Composite XY
-depthLeaf XYA
-depthLeaf XYB
-depthLeaf C

posted on 2018-07-17 18:22  kikajack  阅读(189)  评论(0编辑  收藏  举报