什么是组合模式?

  • 科学术语:将一组对象组合为可像单个对象一样使用的结构。
  • 自我理解:看到组合模式,可以理解成一棵树,树根是接口,其他的都是叶子(具体实现),有的叶子是局部对象,有的是组合对象,这个组合对象就是组合模式的主要实现。可以结合下面的代码来理解。

注意事项

  • 组合模式是将继承用于组合对象的最极端的例子。意思是,这个模式很容易理解,并且你会不自觉的想到处使用它。但其实每一个模式,只是一种策略,它并不适合每一个业务场景。

具体实现

  • 实例场景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  更新时间:2023-11-27 23:47