PHP组合模式处理树形

wen PHP项目 2

本文目录导读:

PHP组合模式处理树形

  1. 什么是组合模式
  2. 核心组件
  3. 完整实现示例
  4. 实际应用场景
  5. 最佳实践
  6. 优缺点

我来详细讲解PHP中的组合模式处理树形结构。

什么是组合模式

组合模式是一种结构型设计模式,它允许将对象组合成树形结构来表示"部分-整体"的层次关系,客户端可以统一地处理单个对象和组合对象。

核心组件

  1. Component(组件):抽象接口,定义通用方法
  2. Leaf(叶子节点):没有子节点的对象
  3. Composite(组合节点):有子节点的对象,实现子节点管理

完整实现示例

基础组合模式

<?php
// 组件接口
interface Component
{
    public function getName(): string;
    public function getSize(): int;
    public function display(int $indent = 0): string;
    public function add(Component $component): void;
    public function remove(Component $component): void;
    public function getChild(int $index): ?Component;
}
// 叶子节点(文件)
class File implements Component
{
    private string $name;
    private int $size;
    public function __construct(string $name, int $size)
    {
        $this->name = $name;
        $this->size = $size;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getSize(): int
    {
        return $this->size;
    }
    public function display(int $indent = 0): string
    {
        return str_repeat("  ", $indent) . "📄 {$this->name} ({$this->size} bytes)\n";
    }
    // 叶子节点没有子节点,这些方法可以空实现或抛异常
    public function add(Component $component): void
    {
        throw new Exception("Cannot add to a file");
    }
    public function remove(Component $component): void
    {
        throw new Exception("Cannot remove from a file");
    }
    public function getChild(int $index): ?Component
    {
        return null;
    }
}
// 组合节点(文件夹)
class Directory implements Component
{
    private string $name;
    private array $children = [];
    public function __construct(string $name)
    {
        $this->name = $name;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getSize(): int
    {
        $totalSize = 0;
        foreach ($this->children as $child) {
            $totalSize += $child->getSize();
        }
        return $totalSize;
    }
    public function display(int $indent = 0): string
    {
        $output = str_repeat("  ", $indent) . "📁 {$this->name}/\n";
        foreach ($this->children as $child) {
            $output .= $child->display($indent + 1);
        }
        return $output;
    }
    public function add(Component $component): void
    {
        $this->children[] = $component;
    }
    public function remove(Component $component): void
    {
        $this->children = array_filter($this->children, function ($child) use ($component) {
            return $child !== $component;
        });
    }
    public function getChild(int $index): ?Component
    {
        return $this->children[$index] ?? null;
    }
    public function getChildren(): array
    {
        return $this->children;
    }
}
// 使用示例
function testFileSystem()
{
    $root = new Directory("root");
    $images = new Directory("images");
    $images->add(new File("logo.png", 5000));
    $images->add(new File("banner.jpg", 10000));
    $docs = new Directory("docs");
    $docs->add(new File("readme.txt", 2000));
    $project = new Directory("project");
    $project->add($images);
    $project->add($docs);
    $project->add(new File("main.php", 8000));
    $root->add($project);
    echo "文件系统结构:\n";
    echo $root->display();
    echo "\n总大小: " . $root->getSize() . " bytes\n";
}
testFileSystem();

增强版:组织架构示例

<?php
// 员工接口
interface EmployeeInterface
{
    public function getName(): string;
    public function getPosition(): string;
    public function getSalary(): float;
    public function getSubordinatesCount(): int;
    public function display(int $indent = 0): string;
    public function add(EmployeeInterface $employee): void;
    public function remove(EmployeeInterface $employee): void;
    public function getTotalSalary(): float;
}
// 员工(叶子节点)
class Employee implements EmployeeInterface
{
    private string $name;
    private string $position;
    private float $salary;
    public function __construct(string $name, string $position, float $salary)
    {
        $this->name = $name;
        $this->position = $position;
        $this->salary = $salary;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getPosition(): string
    {
        return $this->position;
    }
    public function getSalary(): float
    {
        return $this->salary;
    }
    public function getSubordinatesCount(): int
    {
        return 0;
    }
    public function display(int $indent = 0): string
    {
        return str_repeat("  ", $indent) . "👤 {$this->name} - {$this->position} (¥{$this->salary})\n";
    }
    public function add(EmployeeInterface $employee): void
    {
        throw new Exception("Cannot add subordinates to an employee");
    }
    public function remove(EmployeeInterface $employee): void
    {
        throw new Exception("Cannot remove subordinates from an employee");
    }
    public function getTotalSalary(): float
    {
        return $this->salary;
    }
}
// 部门经理(组合节点)
class Manager implements EmployeeInterface
{
    private string $name;
    private string $position;
    private float $salary;
    private array $subordinates = [];
    public function __construct(string $name, string $position, float $salary)
    {
        $this->name = $name;
        $this->position = $position;
        $this->salary = $salary;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getPosition(): string
    {
        return $this->position;
    }
    public function getSalary(): float
    {
        return $this->salary;
    }
    public function getSubordinatesCount(): int
    {
        $count = count($this->subordinates);
        foreach ($this->subordinates as $subordinate) {
            $count += $subordinate->getSubordinatesCount();
        }
        return $count;
    }
    public function display(int $indent = 0): string
    {
        $output = str_repeat("  ", $indent) . "👔 {$this->name} - {$this->position} (¥{$this->salary})\n";
        foreach ($this->subordinates as $subordinate) {
            $output .= $subordinate->display($indent + 1);
        }
        return $output;
    }
    public function add(EmployeeInterface $employee): void
    {
        $this->subordinates[] = $employee;
    }
    public function remove(EmployeeInterface $employee): void
    {
        $this->subordinates = array_filter($this->subordinates, function ($sub) use ($employee) {
            return $sub !== $employee;
        });
    }
    public function getTotalSalary(): float
    {
        $total = $this->salary;
        foreach ($this->subordinates as $subordinate) {
            $total += $subordinate->getTotalSalary();
        }
        return $total;
    }
}
// 使用示例
function testOrganizationStructure()
{
    // 创建组织结构
    $ceo = new Manager("张三", "CEO", 50000);
    $cto = new Manager("李四", "CTO", 35000);
    $cfo = new Manager("王五", "CFO", 30000);
    // 技术团队
    $devLead1 = new Manager("赵六", "开发组长", 25000);
    $devLead2 = new Manager("钱七", "开发组长", 25000);
    $cto->add($devLead1);
    $cto->add($devLead2);
    $devLead1->add(new Employee("孙八", "高级开发", 20000));
    $devLead1->add(new Employee("周九", "中级开发", 15000));
    $devLead2->add(new Employee("吴十", "测试工程师", 18000));
    $devLead2->add(new Employee("郑十一", "运维工程师", 22000));
    // 财务团队
    $cfo->add(new Employee("冯十二", "会计", 12000));
    $cfo->add(new Employee("陈十三", "出纳", 8000));
    $ceo->add($cto);
    $ceo->add($cfo);
    // 展示组织结构
    echo "公司组织结构:\n";
    echo $ceo->display();
    echo "\n公司总人力成本: ¥" . $ceo->getTotalSalary() . "\n";
    echo "公司总人数: " . $ceo->getSubordinatesCount() . "\n";
}
testOrganizationStructure();

高级功能:树形数据遍历

<?php
// 树形数据遍历迭代器
class TreeIterator implements Iterator
{
    private array $nodes = [];
    private int $position = 0;
    public function __construct(Component $root)
    {
        $this->flatten($root);
    }
    private function flatten(Component $node): void
    {
        $this->nodes[] = $node;
        // 如果是组合节点,遍历子节点
        if ($node instanceof Directory) {
            foreach ($node->getChildren() as $child) {
                $this->flatten($child);
            }
        }
    }
    public function current(): Component
    {
        return $this->nodes[$this->position];
    }
    public function key(): int
    {
        return $this->position;
    }
    public function next(): void
    {
        $this->position++;
    }
    public function rewind(): void
    {
        $this->position = 0;
    }
    public function valid(): bool
    {
        return isset($this->nodes[$this->position]);
    }
    public function count(): int
    {
        return count($this->nodes);
    }
}
// 访问者模式示例(可选增强)
interface Visitor
{
    public function visitFile(File $file): void;
    public function visitDirectory(Directory $directory): void;
}
class FileSizeVisitor implements Visitor
{
    private int $totalSize = 0;
    private int $fileCount = 0;
    private int $dirCount = 0;
    public function visitFile(File $file): void
    {
        $this->totalSize += $file->getSize();
        $this->fileCount++;
    }
    public function visitDirectory(Directory $directory): void
    {
        $this->dirCount++;
        foreach ($directory->getChildren() as $child) {
            if ($child instanceof File) {
                $this->visitFile($child);
            } elseif ($child instanceof Directory) {
                $this->visitDirectory($child);
            }
        }
    }
    public function getStats(): array
    {
        return [
            'totalSize' => $this->totalSize,
            'fileCount' => $this->fileCount,
            'dirCount' => $this->dirCount
        ];
    }
}
// 高级使用示例
function testAdvancedFeatures()
{
    // 构建文件系统
    $root = new Directory("root");
    $src = new Directory("src");
    $src->add(new File("app.php", 15000));
    $src->add(new File("config.php", 3000));
    $public = new Directory("public");
    $public->add(new File("index.html", 5000));
    $public->add(new File("style.css", 2000));
    $root->add($src);
    $root->add($public);
    $root->add(new File("README.md", 1000));
    // 使用迭代器遍历
    echo "使用迭代器遍历所有节点:\n";
    $iterator = new TreeIterator($root);
    foreach ($iterator as $index => $node) {
        echo "  [{$index}] {$node->getName()}\n";
    }
    echo "总节点数: " . count($iterator) . "\n\n";
    // 使用访问者统计
    $visitor = new FileSizeVisitor();
    $visitor->visitDirectory($root);
    $stats = $visitor->getStats();
    echo "文件系统统计:\n";
    echo "  文件数量: {$stats['fileCount']}\n";
    echo "  文件夹数量: {$stats['dirCount']}\n";
    echo "  总大小: {$stats['totalSize']} bytes\n";
}
testAdvancedFeatures();

实际应用场景

菜单系统

class MenuItem extends Component
{
    // 菜单项可以是叶子或组合
}
class Menu extends Composite
{
    // 菜单包含子菜单和菜单项
}

权限树

class PermissionNode
{
    // 权限模块可以有子权限
}

分类系统

class Category
{
    // 商品分类支持无限层级
}

最佳实践

  1. 统一接口:叶子节点和组合节点实现相同接口
  2. 安全操作:叶子节点对添加/移除操作抛出异常或静默处理
  3. 递归操作:使用递归处理树形结构
  4. 缓存机制:对于频繁访问的操作可以考虑缓存结果
  5. 性能优化:大型树形结构考虑使用生成器或延迟加载

优缺点

优点:

  • 清晰地表示部分-整体层级关系
  • 客户端可以一致地处理简单和复杂元素
  • 易于增加新类型的组件

缺点:

  • 可能会使设计过于复杂
  • 对类型系统的约束较弱

组合模式非常适合处理树形结构,如文件系统、组织架构、菜单系统等场景。

抱歉,评论功能暂时关闭!