结构型设计模式-组合(对象树) Composite
Tags: designpatterns
Published: 2023-09-06 05:54:03



### 简介

组合模式又叫对象树，将对象按照树形结构串起来，呈现出局部和整体相同的特性。

树中每个节点都由两部分组成，首先节点有自己的业务数据，其次节点内可以含有子节点集合。

比如盒子中可以放物品，也可以放其他小盒子，小盒子里又可以放物品和其他更小的盒子。

当计算盒子的物品价格时，只需要将盒子里本身的物品价格，加上小盒子里所有物品价格即可，递归处理。

![类图](https://deb-gmi.oss-cn-beijing.aliyuncs.com/img/20210408224526.png)

### 角色

- 抽象节点 Component
    
    定义一个节点的基础方法，如内部添加子节点等
    
- 具体节点之叶子节点 Leaf
    
    叶子节点没有子节点
    
- 具体节点之非叶子节点 Composite/Container
    
    非叶子节点，可添加子节点
    

或者也可以不区分是否是叶子节点，都视为节点即可

### 类图

图中显示，Leaf 和 Composite 都实现 Component 接口。

Composite 可添加或删除子节点，execute 则指派调用子节点的 execute 方法。

Leaf 的 execute 是真正执行逻辑的地方

![类图](https://deb-gmi.oss-cn-beijing.aliyuncs.com/img/20210408225100.png)

### 代码

```php
<?php

abstract class Component
{
    protected $parent;

    public function setParent(Component $parent)
    {
        $this->parent = $parent;
    }

    public function getParent(): Component
    {
        return $this->parent;
    }

    public function add(Component $component): void { }

    public function remove(Component $component): void { }

    public function isComposite(): bool
    {
        return false;
    }

    abstract public function operation(): string;
}

class Leaf extends Component
{
    public function operation(): string
    {
        return "Leaf";
    }
}

class Composite extends Component
{
    protected $children;

    public function __construct()
    {
        $this->children = new \SplObjectStorage();
    }

    public function add(Component $component): void
    {
        $this->children->attach($component);
        $component->setParent($this);
    }

    public function remove(Component $component): void
    {
        $this->children->detach($component);
        $component->setParent(null);
    }

    public function isComposite(): bool
    {
        return true;
    }

    public function operation(): string
    {
        $results = [];
        foreach ($this->children as $child) {
            $results[] = $child->operation();
        }

        return "Branch(" . implode("+", $results) . ")";
    }
}

function clientCode(Component $component)
{
    echo "RESULT: " . $component->operation();
}

// 只有一个叶子节点
$simple = new Leaf();
echo "Client: I've got a simple component:\n";
clientCode($simple);

echo "\n";

// 构建一个tree
$tree = new Composite();
$branch1 = new Composite();
$branch1->add(new Leaf());
$branch1->add(new Leaf());
$branch2 = new Composite();
$branch2->add(new Leaf());
$tree->add($branch1);
$tree->add($branch2);
echo "Client: Now I've got a composite tree:\n";
clientCode($tree);

// 合并两个tree
function clientCode2(Component $component1, Component $component2)
{
    if ($component1->isComposite()) {
        $component1->add($component2);
    }
    echo "RESULT: " . $component1->operation();
}

echo "\n";
echo "Client: I don't need to check the components classes even when managing the tree:\n";
clientCode2($tree, $simple);
```

output:

```
Client: I've got a simple component:
RESULT: Leaf
Client: Now I've got a composite tree:
RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf))
Client: I don't need to check the components classes even when managing the tree:
RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf)
```