访问者模式
问题:
使用组合模式开发时,每增加一个操作,就需要在局部类和组合类的各个子类中增加对于新操作的支持,这不但会使类变得越来越臃肿,而且每次都需要对多个类的代码进行修改。
概念:
访问者模式,表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
实现:
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | //单个对象基类 abstract class Unit { protected $depth ; //判断是否为对象集合,从而决定是否支持addUnit()和removeUnit()操作 public function getComposite() { return null; } public function accept(ArmyVisitor $visitor ) { $method = 'visit' . get_class( $this ); $visitor -> $method ( $this ); } protected function setDepth( $depth ) { $this ->depth = $depth ; } public function getDepth() { return $this ->depth; } 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 ; } $unit ->setDepth( $this ->depth + 1); $this ->units[] = $unit ; } public function accept(ArmyVisitor $visitor ) { parent::accept( $visitor ); foreach ( $this ->units as $unit ) { $unit ->accept( $visitor ); } } } //单个对象具体实现 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 ; } } //访问者基类 abstract class ArmyVisitor { //默认的访问方法 abstract public function visit(Unit $node ); public function visitLaserCanon(LaserCanon $node ) { $this ->visit( $node ); } public function visitTroopCarrier(TroopCarrier $node ) { $this ->visit( $node ); } public function visitArmy(Army $node ) { $this ->visit( $node ); } } //具体访问者实现 class TextDumpArmyVisitor extends ArmyVisitor { private $text = '' ; public function visit(Unit $node ) { $ret = '' ; $pad = 4 * $node ->getDepth(); $ret .= sprintf( "%{$pad}s" , '' ); $ret .= get_class( $node ). ':' ; $ret .= 'bombard:' . $node ->bombardStrength(). '<br>' ; $this ->text .= $ret ; } public function getText () { return $this ->text; } } //应用 $army = new Army(); $army ->addUnit( new LaserCanon()); $army ->addUnit( new TroopCarrier()); $army ->addUnit( new Army()); $textDump = new TextDumpArmyVisitor(); $army ->accept( $textDump ); echo $textDump -> getText (); |
效果:
优点:
1. 访问者模式把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。
2. 增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
3. 每增加一个操作,只需在基类中增加一个调用访问者对应方法的方法,无需对多个类进行修改,然后在观察者中实现基于多个类的多个操作方法即可。实质上就是将多个类的操作方法抽取出来放到访问者实现。
缺点:
外部化操作可能会破坏封装。需要在类中为访问者提供额外方法。
【推荐】国内首个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)