zoukankan      html  css  js  c++  java
  • 组合模式

    问题:
    在某些场景下,对于某些类而言,一个独立对象和多个对象组成的集合是没有差别的,即,一个独立对象支持的操作,多个对象组成的集合整体也能支持。
    当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了。

    实现:
    1. 类图示例:

    2. 代码示例:

    //单个对象基类
    abstract class Unit
    {
    	//判断是否为对象集合,从而决定是否支持addUnit()和removeUnit()操作
    	public function getComposite()
    	{
    		return null;
    	}
    	abstract public function bombardStrength();
    }
    //对象组合基类
    abstract class CompositeUnit extends Unit
    {
    	private $units = [];
    	public function getComposite()
    	{
    		return $this;
    	}
    	protected function units()
    	{
    		return $this->units;
    	}
    	public function removeUnit(Unit $unit)
    	{
    		$this->units = array_udiff($this->units, array($unit),
    			function($a, $b) { return $a == $b ? 0 : 1; });
    	}
    	public function addUnit(Unit $unit)
    	{
    		if(in_array($unit, $this->units, true)) {
    			return;
    		}
    		$this->units[] = $unit;
    	}
    }
    //单个对象具体实现
    class Archer extends Unit
    {
    	public function bombardStrength()
    	{
    		return 4;
    	}
    }
    class LaserCanon extends Unit
    {
    	public function bombardStrength()
    	{
    		return 44;
    	}
    }
    //对象组合具体实现
    class TroopCarrier extends CompositeUnit
    {
    	public function bombardStrength()
    	{
    		$ret = 0;
    		foreach($this->units() as $unit) {
    			$ret += $unit->bombardStrength();
    		}
    		return $ret;
    	}
    }
    class Army extends CompositeUnit
    {
    	public function bombardStrength()
    	{
    		$ret = 0;
    		foreach($this->units() as $unit) {
    			$ret += $unit->bombardStrength();
    		}
    		return $ret;
    	}
    }
    //应用
    class UnitScript
    {
    	static function joinExisting(Unit $newUnit, Unit $occupyingUnit) 
    	{
    		$comp;
    		if(!is_null($comp = $occupyingUnit->getComposite())) {
    			$comp->addUnit($newUnit);
    		} else {
    			$comp = new Army();
    			$comp->addUnit($occupyingUnit);
    			$comp->addUnit($newUnit);
    		}
    		return $comp;
    	}
    }
    $obj = UnitScript::joinExisting(new Archer(), new LaserCanon());
    var_dump($obj);
    echo $obj->bombardStrength();
    

    效果:
    优点:
    1. 组合模式中的组合类和局部类都继承自一个共同的基类,支持一个共同的操作集,这样对于组合结构的操作的复杂性都被完全隐藏了,对于客户端来说,操作组合结构与操作一个单独对象一样的简单。
    缺点:
    1. 简化的前提是使所有的类都继承同一个基类,简化的好处有时会以降低对象类型安全为代价。
    2. 若组合结构中出现某些类不支持某些操作(例如示例中的Archer类和LaserCanon类就不支持addUnit()和removeUnit()操作),则需要手动进行类型检查,做出差异化,当这样的情况越来越多的时候,组合模式就开始显得利大于弊了。只有在大部分局部对象可互换的情况下,组合模式才是最适用的。
    3. 组合结构的操作成本可能会是昂贵的。调用组合操作方法,会逐级调用对象树的下级分支的方法(例如示例中的bombardStrength()方法),若下级分支太多,可能会导致系统崩溃。解决办法之一是,在父级对象中缓存计算结果,但需要保证缓存值不会失效,这意味着需要设计一个当对树进行操作时可移除过期缓存的策略,而这也许需要给子对象加上对父对象的引用。
    4. 组合模式很难将数据结构存储在关系型数据库中,但非常适合持久化为XML。

    组合模式的两种实现方式:
    1. 透明方式:所有子类具备完全一致的行为接口,对于外界没有区别(不支持某些操作的子类也需要实现这些操作,只不过可能只是实现抛出异常,没有什么意义)。
    2. 安全方式:不支持某些操作的子类则不去实现这些操作,但是客户端的调用需要做相应的判断(代码示例中实现的就是安全方式)。

  • 相关阅读:
    lsb_release -a 查询Linux系统版本
    html常用标签
    js判断对象是否为空
    springBoot2.x 支持跨域请求配置
    httpclient 上传附件实例
    js 使用sessionStorage总结与实例
    性能测试需求调研分析方法
    jmeter压测实践
    页面加载时调用js函数方法
    IntelliJ IDEA 界面介绍及常用配置
  • 原文地址:https://www.cnblogs.com/wujuntian/p/9623344.html
Copyright © 2011-2022 走看看