组合模式
问题:
在某些场景下,对于某些类而言,一个独立对象和多个对象组成的集合是没有差别的,即,一个独立对象支持的操作,多个对象组成的集合整体也能支持。
当你发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了。
实现:
1. 类图示例:
2. 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | //单个对象基类 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. 安全方式:不支持某些操作的子类则不去实现这些操作,但是客户端的调用需要做相应的判断(代码示例中实现的就是安全方式)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2015-09-10 ThinkPHP缓存
2015-09-10 PHP操作Memcached