💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
# 深度模式-访问者模式(Visitor) ## **模式的定义与特点** 访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。 **优点** 1. 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。 2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。 3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。 4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。 5. 将相关的事物集中到一个访问者对象中。 **缺点** 1. 增加新的元素类(新数据结构)很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。 2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。 3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。 封装某些用于作用于某种数据结构中各元素的操作,可以在不改变数 据结构的前提下定义作用于这些元素的新操作。如银行排号机。 ## **应用场景** 如:排队,排号。 1. 对象结构相对稳定,但其操作算法经常变化的程序。 2. 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。 3. 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。 思路:将类看做一个对象结构,然后划分元素比较元素是否个数稳定 ## **模式的结构** 1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。 2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。 3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。 4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。 5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。 其结构图如图 ![](https://img.kancloud.cn/25/1b/251bb73fa79db4365546555c909ab567_750x665.png) ``` <pre class="calibre10">``` <span class="token1"><</span><span class="token1">?</span>php <span class="token">//抽象访问者</span> interface <span class="token4">Visitor</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">visit</span><span class="token3">(</span>Element $element<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//具体访问者A类</span> class <span class="token4">ConcreteVisitorA</span> implements <span class="token4">Visitor</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">visit</span><span class="token3">(</span>Element $element<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$element <span class="token5">instanceof</span> <span class="token4">ConcreteElementA</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体访问者A访问-->"</span> <span class="token3">.</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">operationA</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token5">if</span> <span class="token3">(</span>$element <span class="token5">instanceof</span> <span class="token4">ConcreteElementB</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体访问者A访问-->"</span> <span class="token3">.</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">operationB</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//具体访问者B类</span> class <span class="token4">ConcreteVisitorB</span> implements <span class="token4">Visitor</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">visit</span><span class="token3">(</span>Element $element<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$element <span class="token5">instanceof</span> <span class="token4">ConcreteElementA</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体访问者B访问-->"</span> <span class="token3">.</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">operationA</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token5">if</span> <span class="token3">(</span>$element <span class="token5">instanceof</span> <span class="token4">ConcreteElementB</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"具体访问者B访问-->"</span> <span class="token3">.</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">operationB</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//抽象元素类</span> interface <span class="token4">Element</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//具体元素A类</span> class <span class="token4">ConcreteElementA</span> implements <span class="token4">Element</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> $visitor<span class="token1">-</span><span class="token1">></span><span class="token4">visit</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">operationA</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token2">"具体元素A的操作。"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//具体元素B类</span> class <span class="token4">ConcreteElementB</span> implements <span class="token4">Element</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> $visitor<span class="token1">-</span><span class="token1">></span><span class="token4">visit</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">operationB</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token2">"具体元素B的操作。"</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//对象结构角色</span> class <span class="token4">ObjectStructure</span> <span class="token3">{</span> private $list <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>list as $key <span class="token1">=</span><span class="token1">></span> $element<span class="token3">)</span> <span class="token3">{</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">add</span><span class="token3">(</span>Element $element<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>list<span class="token3">[</span><span class="token3">]</span> <span class="token1">=</span> $element<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">remove</span><span class="token3">(</span>Element $element<span class="token3">,</span> $changeIndex <span class="token1">=</span> <span class="token6">false</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$changeIndex <span class="token1">===</span> <span class="token6">true</span><span class="token3">)</span> <span class="token3">{</span> $key <span class="token1">=</span> <span class="token4">array_search</span><span class="token3">(</span>$element<span class="token3">,</span> $this<span class="token1">-</span><span class="token1">></span>list<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span>$key <span class="token1">!==</span> <span class="token6">false</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">array_splice</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>list<span class="token3">,</span> $key<span class="token3">,</span> <span class="token6">1</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token5">else</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>list<span class="token3">[</span>$element<span class="token3">]</span><span class="token3">;</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>list as $key <span class="token1">=</span><span class="token1">></span> $value<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$value <span class="token1">===</span> $element<span class="token3">)</span> <span class="token3">{</span> <span class="token4">unset</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>list<span class="token3">[</span>$key<span class="token3">]</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">VisitorPattern</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">//实例化对象结构角色</span> $os <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ObjectStructure</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">add</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConcreteElementA</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">add</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConcreteElementB</span><span class="token3">(</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//实例化具体访问者A类</span> $visitor <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteVisitorA</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor<span class="token3">)</span><span class="token3">;</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"------------------------"</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//实例化具体访问者B类</span> $visitor <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteVisitorB</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> VisitorPattern<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ``` 简单例子: ``` <pre class="calibre10">``` interface <span class="token4">Visitor</span> <span class="token3">{</span> <span class="token">// 抽象访问者角色</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementA</span><span class="token3">(</span>ConcreteElementA $elementA<span class="token3">)</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementB</span><span class="token3">(</span>concreteElementB $elementB<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> interface <span class="token4">Element</span> <span class="token3">{</span> <span class="token">// 抽象节点角色</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> class <span class="token4">ConcreteVisitor1</span> implements <span class="token4">Visitor</span> <span class="token3">{</span> <span class="token">// 具体的访问者1</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementA</span><span class="token3">(</span>ConcreteElementA $elementA<span class="token3">)</span> <span class="token3">{</span><span class="token3">}</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementB</span><span class="token3">(</span>ConcreteElementB $elementB<span class="token3">)</span> <span class="token3">{</span><span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ConcreteVisitor2</span> implements <span class="token4">Visitor</span> <span class="token3">{</span> <span class="token">// 具体的访问者2</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementA</span><span class="token3">(</span>ConcreteElementA $elementA<span class="token3">)</span> <span class="token3">{</span><span class="token3">}</span> public <span class="token5">function</span> <span class="token4">visitConcreteElementB</span><span class="token3">(</span>ConcreteElementB $elementB<span class="token3">)</span> <span class="token3">{</span><span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ConcreteElementA</span> implements <span class="token4">Element</span> <span class="token3">{</span> <span class="token">// 具体元素A</span> private $_name<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$name<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_name <span class="token1">=</span> $name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> <span class="token">// 接受访问者调用它针对该元素的新方法</span> $visitor<span class="token1">-</span><span class="token1">></span><span class="token4">visitConcreteElementA</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ConcreteElementB</span> implements <span class="token4">Element</span> <span class="token3">{</span> <span class="token">// 具体元素B</span> private $_name<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>$name<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_name <span class="token1">=</span> $name<span class="token3">;</span><span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getName</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>_name<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> <span class="token">// 接受访问者调用它针对该元素的新方法</span> $visitor<span class="token1">-</span><span class="token1">></span><span class="token4">visitConcreteElementB</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> class <span class="token4">ObjectStructure</span> <span class="token3">{</span> <span class="token">// 对象结构 即元素的集合</span> private $_collection<span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>_collection <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">attach</span><span class="token3">(</span>Element $element<span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> <span class="token4">array_push</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_collection<span class="token3">,</span> $element<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">detach</span><span class="token3">(</span>Element $element<span class="token3">)</span> <span class="token3">{</span> $index <span class="token1">=</span> <span class="token4">array_search</span><span class="token3">(</span>$element<span class="token3">,</span> $this<span class="token1">-</span><span class="token1">></span>_collection<span class="token3">)</span><span class="token3">;</span> <span class="token5">if</span> <span class="token3">(</span>$index <span class="token1">!==</span> FALSE<span class="token3">)</span> <span class="token3">{</span> <span class="token4">unset</span><span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_collection<span class="token3">[</span>$index<span class="token3">]</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token5">return</span> $index<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>Visitor $visitor<span class="token3">)</span> <span class="token3">{</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>_collection as $element<span class="token3">)</span> <span class="token3">{</span> $element<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">// client</span> $elementA <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteElementA</span><span class="token3">(</span><span class="token2">"ElementA"</span><span class="token3">)</span><span class="token3">;</span> $elementB <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteElementB</span><span class="token3">(</span><span class="token2">"ElementB"</span><span class="token3">)</span><span class="token3">;</span> $elementA2 <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteElementB</span><span class="token3">(</span><span class="token2">"ElementA2"</span><span class="token3">)</span><span class="token3">;</span> $visitor1 <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteVisitor1</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $visitor2 <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ConcreteVisitor2</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $os <span class="token1">=</span> <span class="token5">new</span> <span class="token4">ObjectStructure</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$elementA<span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$elementB<span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">attach</span><span class="token3">(</span>$elementA2<span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">detach</span><span class="token3">(</span>$elementA<span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor1<span class="token3">)</span><span class="token3">;</span> $os<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$visitor2<span class="token3">)</span><span class="token3">;</span> ``` ``` 例子: ``` <pre class="calibre17">``` <span class="token">/* 访问者模式的简单例子 我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。 而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。 首先我们给出单子的接口,它只有一个方法accept。 */</span> <span class="token">//单个单子的接口(相当于Element)</span> interface <span class="token4">Bill</span> <span class="token3">{</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>AccountBookViewer $viewer<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//其中的方法参数AccountBookViewer是一个账本访问者接口,接下来也就是实现类,收入单子和消费单子,或者说收入和支出类。</span> <span class="token">//消费的单子(支出类)</span> class <span class="token4">ConsumeBill</span> implements <span class="token4">Bill</span> <span class="token3">{</span> private $amount <span class="token1">=</span> <span class="token6">0</span><span class="token3">;</span> <span class="token">//支出费用</span> private $item <span class="token1">=</span> <span class="token2">""</span><span class="token3">;</span> <span class="token">//支出项没有名称</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>float $amount<span class="token3">,</span> String $item<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>amount <span class="token1">=</span> $amount<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>item <span class="token1">=</span> $item<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>AccountBookViewer $viewer<span class="token3">)</span> <span class="token3">{</span> <span class="token">//$viewer->view($this);</span> $viewer<span class="token1">-</span><span class="token1">></span><span class="token4">viewConsume</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getAmount</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>amount<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getItem</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>item<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//收入单子(收入类)</span> class <span class="token4">IncomeBill</span> implements <span class="token4">Bill</span> <span class="token3">{</span> private $amount <span class="token1">=</span> <span class="token6">0</span><span class="token3">;</span> private $item <span class="token1">=</span> <span class="token2">""</span><span class="token3">;</span> public <span class="token5">function</span> <span class="token4">__construct</span><span class="token3">(</span>float $amount<span class="token3">,</span> String $item<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>amount <span class="token1">=</span> $amount<span class="token3">;</span> $this<span class="token1">-</span><span class="token1">></span>item <span class="token1">=</span> $item<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">accept</span><span class="token3">(</span>AccountBookViewer $viewer<span class="token3">)</span> <span class="token3">{</span> <span class="token">//$viewer->view($this);</span> $viewer<span class="token1">-</span><span class="token1">></span><span class="token4">viewIncome</span><span class="token3">(</span>$this<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getAmount</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>amount<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getItem</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>item<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//上面最关键的还是里面的accept方法,它直接让访问者访问自己,这相当于一次静态分派(文章最后进行解释),当然我们也可以不使用重载而直接给方法不同的名称。</span> <span class="token">//账单查看者接口(相当于Visitor)</span> interface <span class="token4">AccountBookViewer</span> <span class="token3">{</span> <span class="token">//查看消费的单子</span> public <span class="token5">function</span> <span class="token4">viewConsume</span><span class="token3">(</span>ConsumeBill $bill<span class="token3">)</span><span class="token3">;</span> <span class="token">//查看收入的单子</span> <span class="token5">function</span> <span class="token4">viewIncome</span><span class="token3">(</span>IncomeBill $bill<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token">//注册会计师类,查看账本的类之一</span> class <span class="token4">CPA</span> implements <span class="token4">AccountBookViewer</span> <span class="token3">{</span> <span class="token">//注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没</span> public <span class="token5">function</span> <span class="token4">viewConsume</span><span class="token3">(</span>ConsumeBill $bill<span class="token3">)</span> <span class="token3">{</span> <span class="token5">if</span> <span class="token3">(</span>$bill<span class="token1">-</span><span class="token1">></span><span class="token4">getItem</span><span class="token3">(</span><span class="token3">)</span> <span class="token1">===</span> <span class="token2">"工资"</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"注会查看工资是否交个人所得税。"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//如果是收入,则所有的收入都要交税</span> public <span class="token5">function</span> <span class="token4">viewIncome</span><span class="token3">(</span>IncomeBill $bill<span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"注会查看收入交税了没。"</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//老板类,查看账本的类之一 老板只关心收入和支出的总额,注册会计师只关注该交税的是否交税</span> class <span class="token4">Boss</span> implements <span class="token4">AccountBookViewer</span> <span class="token3">{</span> private $totalIncome <span class="token1">=</span> <span class="token6">0</span><span class="token3">;</span> private $totalConsume <span class="token1">=</span> <span class="token6">0</span><span class="token3">;</span> <span class="token">//老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心</span> public <span class="token5">function</span> <span class="token4">viewConsume</span><span class="token3">(</span>ConsumeBill $bill<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>totalConsume <span class="token1">+</span><span class="token1">=</span> $bill<span class="token1">-</span><span class="token1">></span><span class="token4">getAmount</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">viewIncome</span><span class="token3">(</span>IncomeBill $bill<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>totalIncome <span class="token1">+</span><span class="token1">=</span> $bill<span class="token1">-</span><span class="token1">></span><span class="token4">getAmount</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getTotalIncome</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"老板查看一共收入多少,数目是:"</span> <span class="token3">.</span> $this<span class="token1">-</span><span class="token1">></span>totalIncome<span class="token3">)</span><span class="token3">;</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>totalIncome<span class="token3">;</span> <span class="token3">}</span> public <span class="token5">function</span> <span class="token4">getTotalConsume</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token4">print_r</span><span class="token3">(</span><span class="token2">"老板查看一共花费多少,数目是:"</span> <span class="token3">.</span> $this<span class="token1">-</span><span class="token1">></span>totalConsume<span class="token3">)</span><span class="token3">;</span> <span class="token5">return</span> $this<span class="token1">-</span><span class="token1">></span>totalConsume<span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//账本类(相当于ObjectStruture对象结构)</span> class <span class="token4">AccountBook</span> <span class="token3">{</span> <span class="token">//单子列表</span> private $billList <span class="token1">=</span> <span class="token4">array</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//添加单子</span> public <span class="token5">function</span> <span class="token4">addBill</span><span class="token3">(</span>Bill $bill<span class="token3">)</span> <span class="token3">{</span> $this<span class="token1">-</span><span class="token1">></span>billList<span class="token3">[</span><span class="token3">]</span> <span class="token1">=</span> $bill<span class="token3">;</span> <span class="token3">}</span> <span class="token">//供账本的查看者查看账本</span> public <span class="token5">function</span> <span class="token4">show</span><span class="token3">(</span>AccountBookViewer $viewer<span class="token3">)</span> <span class="token3">{</span> foreach <span class="token3">(</span>$this<span class="token1">-</span><span class="token1">></span>billList as $key <span class="token1">=</span><span class="token1">></span> $bill<span class="token3">)</span> <span class="token3">{</span> $bill<span class="token1">-</span><span class="token1">></span><span class="token4">accept</span><span class="token3">(</span>$viewer<span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">//测试客户端</span> class <span class="token4">Client</span> <span class="token3">{</span> public static <span class="token5">function</span> <span class="token4">main</span><span class="token3">(</span><span class="token3">)</span> <span class="token3">{</span> <span class="token">//账本类(相当于ObjectStruture对象结构)</span> $accountBook <span class="token1">=</span> <span class="token5">new</span> <span class="token4">AccountBook</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//添加两条收入</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">addBill</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">IncomeBill</span><span class="token3">(</span><span class="token6">10000</span><span class="token3">,</span> <span class="token2">"卖商品"</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">addBill</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">IncomeBill</span><span class="token3">(</span><span class="token6">12000</span><span class="token3">,</span> <span class="token2">"卖广告位"</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//添加两条支出</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">addBill</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConsumeBill</span><span class="token3">(</span><span class="token6">1000</span><span class="token3">,</span> <span class="token2">"工资"</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">addBill</span><span class="token3">(</span><span class="token5">new</span> <span class="token4">ConsumeBill</span><span class="token3">(</span><span class="token6">2000</span><span class="token3">,</span> <span class="token2">"材料费"</span><span class="token3">)</span><span class="token3">)</span><span class="token3">;</span> $boss <span class="token1">=</span> <span class="token5">new</span> <span class="token4">Boss</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//账单查看者接口AccountBookViewer</span> $cpa <span class="token1">=</span> <span class="token5">new</span> <span class="token4">CPA</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token">//账单查看者接口AccountBookViewer</span> <span class="token">//两个访问者分别访问账本</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">show</span><span class="token3">(</span>$cpa<span class="token3">)</span><span class="token3">;</span> <span class="token">//会计查看账本</span> $accountBook<span class="token1">-</span><span class="token1">></span><span class="token4">show</span><span class="token3">(</span>$boss<span class="token3">)</span><span class="token3">;</span> <span class="token">//boss查看账本</span> $boss<span class="token1">-</span><span class="token1">></span><span class="token4">getTotalConsume</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> $boss<span class="token1">-</span><span class="token1">></span><span class="token4">getTotalIncome</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> <span class="token3">}</span> <span class="token3">}</span> <span class="token">/* 上面的代码中,可以这么理解,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。 访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。 如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。 */</span> Client<span class="token3">:</span><span class="token3">:</span><span class="token4">main</span><span class="token3">(</span><span class="token3">)</span><span class="token3">;</span> ``` ```