php设计模式(十):组合模式(Composite)
- 陈大剩
- 2023-04-21 21:02:53
- 755
组合模式
组合模式又称:对象树、Object Tree、Composite,组合 是一种结构型设计模式,使用它将对组合成树状结构,并且能像使用独立对象一样使用它们。
问题
学过 Linux 的同学都知道,Linux 一切都是文件,那么 Linux 文件系统类型就有两类对象: 文件夹 和 文件 。一个 文件夹 中可以包含多个 文件 或者几个较小的 文件夹 。这些 小文件夹 中同样可以包含一些 文件 或更小的 文件夹 ,以此类推。如果是我们来开发 Linux 文件系统,我们该如何做出文件结构呢?
打开所有文件夹, 找到每件文件, 然后 统计。这在真实世界中或许可行,但在程序中,并不能简单地使用循环语句来完成该工作。必须事先知道所有 文件夹 和 文件 的类别,所有文件夹的嵌套层数以及其他繁杂的细节信息。因此, 直接计算极不方便, 甚至完全不可行。
如果应用的核心模型能用树状结构表示,在应用中使用组合模式才有价值。
解决方法
使用一个通用接口来与 文件夹 和 文件 进行交互, 并且在该接口中声明一个统计子文件的方法。我们可以使用组合模式以递归方式处理文件夹对象树中的所有项目。递归出所有内部组成部分。
本例使用透明的组合模式,还有安全组合模式可用。
结构
Node:包含文件夹(树枝节点)和文件(叶子节点)方法的抽象类
Dir:文件夹(树枝节点)有子节点
File:文件(叶子节点)没有子节点
代码示例
抽象类容器类(节点类)
abstract class Node
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
/**
* 增加节点
* @return mixed
* @author chendashengpc
*/
abstract public function add(Node $node);
/**
* 删除节点
* @return mixed
* @author chendashengpc
*/
abstract public function remove(Node $node);
/**
* 显示当前树
* @param $level 树等级
* @return mixed
* @author chendashengpc
*/
abstract public function display($level);
}
具体容器类(文件夹)
/**
* 文件夹
*/
class Dir extends Node
{
protected $children = [];
public function add(Node $node)
{
if (isset($this->children[$node->name])) {
throw new \Exception('请勿重复创建');
}
$this->children[$node->name] = $node;
}
public function remove(Node $node)
{
if (!isset($this->children[$node->name])) {
throw new \Exception('请创建后再移除');
}
unset($this->children[$node->name]);
}
/**
* 输出目录 [d] 为目录
* @param $level
* @return string
* @author chendashengpc
*/
public function display($level = '')
{
$nameStr = $level . '[d]' . $this->name . PHP_EOL;
foreach ($this->children as $k => $v) {
$nameStr .= $v->display($level . '--');
}
return $nameStr;
}
}
叶节点类(文件)
/**
* 文件类
*/
class File extends Node
{
public function add(Node $node)
{
throw new \Exception('文件不能添加子节点');
}
public function remove(Node $node)
{
throw new \Exception('文件不能添加子节点');
}
/**
* 输出文件 [-] 为文件
* @param $level
* @return string
* @author chendashengpc
*/
public function display($level = '')
{
return $level . '[-]' . $this->name . PHP_EOL;
}
}
客户端代码
$designPatterns = new Dir('design patterns');
$readme = new File('README.md');
$designPatterns->add($readme);
/**
* Composite 文件夹
*/
$composite = new Dir('Composite');
$node = new File('Node.php');
$file = new File('File.php');
$dir = new File('Dir.php');
$composite->add($node);
$composite->add($file);
$composite->add($dir);
$designPatterns->add($composite);
$composite->remove($dir);
/**
* Singleton 文件夹
*/
$singleton = new Dir('Singleton');
$singletonFile = new File('Singleton.php');
$singleton->add($singletonFile);
/**
* 测试文件夹
*/
$test = new Dir('test');
$testFile = new File('test-file');
$test->add($testFile);
$singleton->add($test);
$designPatterns->add($singleton);
echo $designPatterns->display();
输出
[d]design patterns
--[-]README.md
--[d]Composite
----[-]Node.php
----[-]File.php
--[d]Singleton
----[-]Singleton.php
----[d]test
------[-]test-file
UML
优缺点
优点
- 可以利用递归机制更方便的使用复杂结合
- 开闭原则。无需更改现有代码,你就可以在应用中添加新元素,使其成为对象树的一部分。
缺点
- 对于功能差异较大的类,提供公共接口或许会有困难。 在特定情况下,需要过度一般化组件接口,使其变得令人难以理解。