什么是组合模式?
- 科学术语:将一组对象组合为可像单个对象一样使用的结构。
- 自我理解:看到组合模式,可以理解成一棵树,树根是接口,其他的都是叶子(具体实现),有的叶子是局部对象,有的是组合对象,这个组合对象就是组合模式的主要实现。可以结合下面的代码来理解。
注意事项
- 组合模式是将继承用于组合对象的最极端的例子。意思是,这个模式很容易理解,并且你会不自觉的想到处使用它。但其实每一个模式,只是一种策略,它并不适合每一个业务场景。
具体实现
- 实例场景1:宝马公司的老板要求财务出一个报表,能够实时的看到世界各地工厂的收支情况。(最终会交给IT部门)
- 这个需求的目标很简单,通过各工厂提供的财务收支表,最后分类汇总即可。
- 实例场景2:开发游戏的过程中,敌对双方的血槽值/战斗力实时计算模块。
- 战斗单元是基础单元接口,其余实现类要么是局部对象(单一功能),要么是组合对象。
- UML图 
代码实例
因篇幅有限,代码只实现场景1,有兴趣的朋友可以自己实现场景2
Settle.php
<?php
namespace com\wdqz\combination;
/**
 * 组合模式-结算接口
 * - 宝马公司的需求是收支结算,这里结算工具将成为各分厂的基本单元
 *
 * @author woodhead
 * @date 2023年11月24日16:25:14
 */
interface Settle
{
    /**
     * 添加分厂结算
     * @return mixed
     */
    public function addSettle(Settle $settle);
    /**
     * 收入
     * @return mixed
     */
    public function revenue();
    /**
     * 支出
     * @return mixed
     */
    public function expend();
    /**
     * 计算收支差
     * @return mixed
     */
    public function calc();
}BeijingFactory.php
<?php
namespace com\wdqz\combination;
/**
 * 宝马北京工厂
 *
 * @author woodhead
 * @date 2023年11月24日16:28:33
 */
class BeijingFactory implements Settle
{
    /**
     * 添加分厂结算
     * - 因自身已经是分厂,不再执行具体逻辑
     *
     * @param Settle $settle
     * @return false
     */
    public function addSettle(Settle $settle)
    {
        return false;
    }
    /**
     * 收入金额
     * @return float
     */
    public function revenue()
    {
        return 5000000.89;
    }
    /**
     * 支出金额
     * @return float
     */
    public function expend()
    {
        return 3000000.32;
    }
    /**
     * 计算收支差
     * @return float
     */
    public function calc()
    {
        return floatval(bcsub($this->revenue($this), $this->expend($this), 2));
    }
}ShanghaiFactory.php
<?php
namespace com\wdqz\combination;
/**
 * 宝马上海工厂
 *
 * @author woodhead
 * @date 2023年11月24日16:28:33
 */
class ShanghaiFactory implements Settle
{
    /**
     * 添加分厂结算
     * - 因自身已经是分厂,不再执行具体逻辑
     *
     * @param Settle $settle
     * @return false
     */
    public function addSettle(Settle $settle)
    {
        return false;
    }
    /**
     * 收入金额
     * @return float
     */
    public function revenue()
    {
        return 8000000.43;
    }
    /**
     * 支出金额
     * @return float
     */
    public function expend()
    {
        return 2000000.98;
    }
    /**
     * 计算收支差
     * @return float
     */
    public function calc()
    {
        return floatval(bcsub($this->revenue($this), $this->expend($this), 2));
    }
}HeadFactory.php
<?php
namespace com\wdqz\combination;
/**
 * 总厂
 * - 组合模式的核心
 * - 负责局部对象的整合
 * - 负责最终整体结算
 */
class HeadFactory implements Settle
{
    /**
     * @var array 分厂列表
     */
    private $factoryList = [];
    /**
     * 添加分厂结算
     * - 总厂将组合分厂
     *
     * @return void
     */
    public function addSettle(Settle $settle)
    {
        if (in_array($settle, $this->factoryList, true)) {
            return;
        }
        $this->factoryList[] = $settle;
    }
    /**
     * 收入金额
     * @return float
     */
    public function revenue()
    {
        $resultAmount = 0.00;
        foreach ($this->factoryList as $factory) {
            $resultAmount += $factory->revenue();
        }
        return $resultAmount;
    }
    /**
     * 支出金额
     * @return float
     */
    public function expend()
    {
        $resultAmount = 0.00;
        foreach ($this->factoryList as $factory) {
            $resultAmount += $factory->expend();
        }
        return $resultAmount;
    }
    /**
     * 计算收支差
     * @return float
     */
    public function calc()
    {
        $resultAmount = 0.00;
        foreach ($this->factoryList as $factory) {
            $resultAmount += $factory->calc();
        }
        return $resultAmount;
    }
}test.php
<?php
require_once('../../../autoloader.php');
use com\wdqz\combination\HeadFactory;
use com\wdqz\combination\BeijingFactory;
use com\wdqz\combination\ShanghaiFactory;
// 总厂对象
$headFactory = new HeadFactory();
// 添加北京分厂结算对象
$headFactory->addSettle(new BeijingFactory());
// 添加上海分厂结算对象
$headFactory->addSettle(new ShanghaiFactory());
// 计算公司收支+利润
echo '宝马公司本年度总收入:'.$headFactory->revenue().PHP_EOL;
echo '宝马公司本年度总支出:'.$headFactory->expend().PHP_EOL;
echo '宝马公司本年度总利润:'.$headFactory->calc().PHP_EOL;效果
作者:Wolf  创建时间:2023-11-24 00:31
最后编辑:Wolf 更新时间:2025-07-15 12:07
最后编辑:Wolf 更新时间:2025-07-15 12:07
