访问者模式

问题:
使用组合模式开发时,每增加一个操作,就需要在局部类和组合类的各个子类中增加对于新操作的支持,这不但会使类变得越来越臃肿,而且每次都需要对多个类的代码进行修改。

概念:
访问者模式,表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

实现:

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. 每增加一个操作,只需在基类中增加一个调用访问者对应方法的方法,无需对多个类进行修改,然后在观察者中实现基于多个类的多个操作方法即可。实质上就是将多个类的操作方法抽取出来放到访问者实现。
缺点:
外部化操作可能会破坏封装。需要在类中为访问者提供额外方法。

posted @   疯一样的狼人  阅读(107)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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)
点击右上角即可分享
微信分享提示