访问者模式
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
介绍
意图:主要将数据结构与数据操作分离。
主要解决:稳定的数据结构和易变的操作耦合问题。
何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。
如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。
应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。
优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。
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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
<?php abstract class Unit{ protected $depth = 0; function accept(ArmyVisitor $visitor){ $method = "visit" . get_class($this); $visitor->$method($this); } protected function setDepth($depth){ $this->depth = $depth; } function getDepth(){ return $this->depth; } abstract function bombardStrength(); } //射手 class Archer extends Unit{ //轰炸强度,战斗力 function bombardStrength(){ return 2; } } //骑兵 class Cavalry extends Unit{ function bombardStrength(){ return 4; } } //激光大炮 class LaserCannonUnit extends Unit{ function bombardStrength(){ return 6; } } //部队运输 class TroopCarrierUnit extends Unit{ function bombardStrength(){ return 18; } } //组合单元 abstract class ContainerUnit extends Unit{ protected $units = []; function addUnit(Unit $unit){ foreach($this->units as $thisUnit){ if($unit === $thisUnit){ return ; } } $unit->setDepth($this->depth + 1); $this->units[] = $unit; } function accept(ArmyVisitor $visitor){ $method = "visit" . get_class($this); $visitor->$method($this); foreach($this->units as $thisUnit){ $thisUnit->accept($visitor); } } } class Army extends ContainerUnit{ //总战斗力 function bombardStrength(){ $ret = 0; foreach($this->units as $unit){ $ret += $unit->bombardStrength(); } return $ret; } } //军队访问者 abstract class ArmyVisitor{ abstract function visit(Unit $node); function visitArmy(Army $node){ //调用各自子类的访问方法 $this->visit($node); } function visitArcher(Archer $node){ $this->visit($node); } function visitCavalry(Cavalry $node){ $this->visit($node); } function visitLaserCannonUnit(LaserCannonUnit $node){ $this->visit($node); } function visitTroopCarrierUnit(TroopCarrierUnit $node){ $this->visit($node); } } //军队攻击力访问者 class TextDumpArmyVisitor extends ArmyVisitor{ private $text = ""; //收集传入军队的攻击力 //就是输出结构 function visit(Unit $node){ $ret = ""; $pad = 4 * $node->getDepth(); $ret .= sprintf("%{$pad}s",""); $ret .= get_class($node).": "; $ret .= "bombard: " . $node->bombardStrength() . "\n"; $this->text .=$ret; /*$this->text .= $node->bombardStrength()."<br />";*/ /*格式如下 * Army: bombard: 30 Archer: bombard: 2 Cavalry: bombard: 4 LaserCannonUnit: bombard: 6 TroopCarrierUnit: bombard: 18*/ } function getText(){ return $this->text; } } $main_army = new Army(); $main_army->addUnit(new Archer()); $main_army->addUnit(new Cavalry()); $main_army->addUnit(new LaserCannonUnit()); $main_army->addUnit(new TroopCarrierUnit()); $text_dump_visitor = new TextDumpArmyVisitor(); $main_army->accept($text_dump_visitor); print $text_dump_visitor->getText(); //税务官 class TaxCollectionVisitor extends ArmyVisitor{ private $due = 0; private $report = ""; function visit(Unit $node){ $this->levy($node,1); } function visitArcher(Archer $node){ $this->levy($node,2); } function visitCavalry(Cavalry $node){ $this->levy($node,3); } function visitTroopCarrierUnit(TroopCarrierUnit $node){ $this->levy($node,5); } private function levy(Unit $unit,$amount){ $this->report .= "Tax levied for ".get_class($unit); $this->report .= ": $amount\n"; $this->due += $amount; } function getReport(){ return $this->report; } function getTax(){ return $this->due; } } $texVisitor = new TaxCollectionVisitor(); $main_army->accept($texVisitor); print $texVisitor->getReport(); |
输出结果