🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
组合(Composite)模式:有时又叫作部分-整体模式。将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。 **主要优点有:** 1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”; **主要缺点是:** 1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系; 2. 不容易限制容器中的构件; 3. 不容易用继承的方法来增加构件的新功能; 常见使用场景:如树形菜单、文件夹菜单、部门组织架构图、窗体程序中的简单控件与容器控件等 #### **模式的结构** 组合模式包含以下主要角色。 1. **抽象构件(Component)角色**:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。 2. **树叶构件(Leaf)角色**:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。 3. **树枝构件(Composite)角色**:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。 组合模式分为透明式的组合模式和安全式的组合模式。 **透明式的组合模式** ``` /** *透明式合成模式 *抽象构件 */ interface Component { public function getComposite(); public function operation(); public function add(Component $component); public function remove(Component $component); public function getChildComposite(); } /** * 树枝构件 */ class Composite implements Component { private $_children_composites=array(); //返回自己的实例 public function getComposite() { return $this; } // 示例方法,调用各个子对象的operation方法 public function operation() { foreach ($this->_children_composites as $composite) { $composite->operation(); } } //聚集管理方法 添加一个子对象 public function add(Component $component) { $this->_children_composites[] = $component; } //聚集管理方法 删除一个子对象 public function remove(Component $component) { foreach ($this->_children_composites as $key => $row) { if ($component == $row) { unset($this->_children_composites[$key]); return TRUE; } } return FALSE; } // 聚集管理方法 返回所有的子对象 public function getChildComposite() { return $this->_children_composites; } } /** * 树叶构件 */ class Leaf implements Component { private $_name; public function __construct($name) { $this->_name = $name; } public function operation() { echo $this->_name."<br>"; } public function getComposite() { return null; } public function add(Component $component) { return FALSE; } public function remove(Component $component) { return FALSE; } public function getChildComposite() { return null;//树叶构件无子节点 } } class CompositePattern{ public static function main(){ // client $leaf1 = new Leaf('first'); $leaf2 = new Leaf('second'); $composite = new Composite(); $composite->add($leaf1); $composite->add($leaf2); $composite->operation(); $composite->remove($leaf2); $composite->operation(); } } CompositePattern::main(); ``` **安全式的组合模式** ``` /** * *安全式合成模式 */ interface Component { public function getComposite(); //返回自己的实例 public function operation(); } class Composite implements Component { // 树枝组件角色 private $_composites=array(); public function getComposite() { return $this; } public function operation() { foreach ($this->_composites as $composite) { $composite->operation(); } } public function add(Component $component) { //聚集管理方法 添加一个子对象 $this->_composites[] = $component; } public function remove(Component $component) { // 聚集管理方法 删除一个子对象 foreach ($this->_composites as $key => $row) { if ($component == $row) { unset($this->_composites[$key]); return TRUE; } } return FALSE; } public function getChild() { // 聚集管理方法 返回所有的子对象 return $this->_composites; } } class Leaf implements Component { private $_name; public function __construct($name) { $this->_name = $name; } public function operation() { echo $this->_name."被访问了<br>"; } public function getComposite() { return null; } } class CompositePattern{ public static function main(){ // client $leaf1 = new Leaf('first'); $leaf2 = new Leaf('second'); $composite = new Composite(); $composite->add($leaf1); $composite->add($leaf2); $composite->operation(); echo "-----------<br />"; $composite->remove($leaf2); $composite->operation(); } } CompositePattern::main(); 结果: first被访问了 second被访问了 ----------- first被访问了 ``` ## **组合模式的应用场景** 1. 在需要表示一个对象整体与部分的层次结构的场合。 2. 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。 ``` //抽象构件 Abstract class Component{ public $name; abstract function doSomething(); public function __construct($name){ $this->name = $name; } } //总经理 部门经理 主管等 树枝构件 class Composite extends Component{ public $lever = 1; public $c_nodes = array(); public function getChildComposite(){ echo "<pre>"; print_r($this->c_nodes); } //添加子节点 public function add(Component $component){ $component->lever = $this->lever + 1; $this->c_nodes[] = $component; } public function doSomething(){ $nbsp=str_repeat("&nbsp;",($this->lever-1)*4); echo "{$nbsp}我是层级{$this->lever}:{$this->name}<br>"; } } //普通员工 树叶构件 不能添加子节点 class Leaf extends Component{ public $lever; public function doSomething(){ $nbsp=str_repeat("&nbsp;",($this->lever-1)*4); echo "{$nbsp}层级{$this->lever}:{$this->name}(work)<br>"; } } $manager = new Composite("总经理"); $xgm = new Composite("销售经理"); $sgm = new Composite("行政经理"); $staff = new Leaf("张三"); //组装成树 $manager->add($xgm); $manager->add($sgm); $sgm->add($staff); $staff2 = new Leaf("李四"); $sgm->add($staff2); $staff3 = new Leaf("王五"); $xgm->add($staff3); $manager->getChildComposite(); //遍历树 - 函数 function display(Composite $composite){ $composite->doSomething(); foreach($composite->c_nodes as $c_node){ //属于枝叶执行doSomething 否则继续回调displa函数 $c_node instanceof Leaf ? $c_node->doSomething() : display($c_node); } } display($manager); ``` 结果: ~~~ Array ( [0] => Composite Object ( [lever] => 2 [c_nodes] => Array ( [0] => Leaf Object ( [lever] => 3 [name] => 王五 ) ) [name] => 销售经理 ) [1] => Composite Object ( [lever] => 2 [c_nodes] => Array ( [0] => Leaf Object ( [lever] => 3 [name] => 张三 ) [1] => Leaf Object ( [lever] => 3 [name] => 李四 ) ) [name] => 行政经理 ) ) 我是层级1:总经理     我是层级2:销售经理         层级3:王五(work)     我是层级2:行政经理         层级3:张三(work)         层级3:李四(work) ~~~ 实例2: ![](https://img.kancloud.cn/5c/4d/5c4d2aab0b864b3819857f722ed23c7c_486x321.png) ``` <?php //模型中所有的类都扩展自 Unit 类,同时,Army 和 TroopCarrier 类被设计成了组合对象,用于包含 Unit 对象。 //但是,我们也发现 ArcherUnit 和 LaserCannonUnit 类(称为局部对象,也称为树叶对象,因为组合模式为树形结构,组合对象为树干,单独存在的对象为树叶,树叶对象应为最小单元,其中不能包含其他对象。)不能包含其他 Unit 对象,但也实现了addUnit() 方法,这个问题我们后面讨论。 /* 战斗单元的抽象类 有一个抽象方法bombardStrength()方法用于设置战斗单元的攻击强度 */ abstract class Unit{ function getComposite(){ return null; } abstract function bombardStrength(); } //弓箭手 class Archer extends Unit{ function bombardStrength(){ return 4; } } //激光炮单位 class LaserCanonUnit extends Unit{ function bombardStrength(){ return 44; } } /* CompositeUnit 混合作战单元类 因为CompositeUnit没有实现bombardStrength()方法, 所以CompositeUnit声明为抽象类abstract 战斗单元为树叶类时为了避免使用addUnit与removeUnit则通过下面抽象出的CompositeUnit类的getComposite */ abstract class CompositeUnit extends Unit{ private $units=array(); function getComposite(){ return $this; } protected function units(){ return $this->units; } function removeUnit(Unit $unit){ $units=array(); foreach ($this->units as $thisunit){ if(!unit!==$thisunit){ $units[]=$thisunit; } } $this->units=$units; } function addUnit(Unit $unit){ if(in_array($unit, $this->units,true)){ return; } $this->units[]=$unit; } } /** * 定义一个独立的类(Army类)来组合战斗单元。 * 战斗集合的类 树枝类 * 如果 Army(军队)类需要和其他Army类进行合并,同时,每个 Army 都有自己的 ID,这样 Army 在以后还可以从整编中解散出来。 * 我们修改原来的 Army(军队)类,使其同时支持 Unit(单元)和 Army(军队)。 */ class Army extends CompositeUnit{ public $units=array(); //添加单位 function addUnit(Unit $unit){ if(in_array($unit, $this->units,true)){ return; } $this->units[]=$unit; } //移除单位 function removeUnit(Unit $unit){ $units=array(); foreach ($this->units as $thisunit){ if($unit!==$thisunit){ $units[]=$thisunit; } } $this->units=$thisunit; } //总攻击强度 function bombardStrength(){ $ret=0; foreach ($this->units as $unit){ $ret+=$unit->bombardStrength(); } return $ret; } //添加陆军 function addArmy(Army $army){ array_push($this->armies, $army); } } //运兵船 class TroopCarrier extends CompositeUnit{ function addUnit(Unit $unit){ if($unit instanceof Cavalry){//骑兵对象 throw new UnitException("不能将骑兵带上船"); } Parent::addUnit($unit); } function bombardStrength(){ return 0; } } //骑兵对象 class Cavalry extends CompositeUnit{ function addUnit(Unit $unit){ } function removeUnit(Unit $unit){ } function bombardStrength(){ } } class UnitException extends Exception{ } class UnitScript{ //有两个Unit类型的参数,第一个是新的Unit对象,第二个是之前的Unit对象 static function joinExisting(Unit $newUnit,Unit $oldUnit){ $comp; if(!is_null($comp=$oldUnit->getComposite())){ //如果第二个Unit对象是一个CompositeUnit对象,那么直接add $comp->addUnit($newUnit); }else { //如果第二个Unit对象不是一个CompositeUnit对象,那么创建一个Army对象,将两个Unit存入这个Army对象 $comp=new Army(); $comp->addUnit($oldUnit); $comp->addUnit($newUnit); } return $comp; } } $main_army=new Army(); $main_army->addUnit(new Archer()); $main_army->addUnit(new LaserCanonUnit()); $sub_army=new Army(); $sub_army->addUnit(new Archer()); $sub_army->addUnit(new Archer()); $sub_army->addUnit(new Archer()); $main_army->addUnit($sub_army); $a=new TroopCarrier(); $b=$a->addUnit(new Archer()); //$b=$a->addUnit(new Cavalry()); //print_r($main_army->units); print "攻击强度:{$main_army->bombardStrength()}<br>"; //也可以这样 UnitScript::joinExisting($main_army, $sub_army); $main=new Army(); $main->addUnit(new Archer()); $sub=new Army(); $sub->addUnit(new LaserCanonUnit()); $all=UnitScript::joinExisting($main, $sub); print "攻击强度:{$all->bombardStrength()}<br>"; ``` 结果: ``` 攻击强度:60 攻击强度:48 ```