PHP设计模式之访问者模式

访问者模式构造了包含某个算法截然不同的对象,访问者模式的一个主要优点是能够在不更改对象的情况下就向该对象添加新的功能。

博客系统功能第一版:普通博客

demo1.php

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
<?php
header('Content-Type:text/html; charset=utf-8');

class Blog{
private $user;
private $date;

public function __construct($user, $date){
$this->user = $user;
$this->date = $date;
}

public function addBlog(){
return $this->user.'于'.$this->date.'发表了一篇无博文!';
}
}

$blog1 = new Blog('suse', '2015年3月2日');
echo $blog1->addBlog();

echo '<br />';

$blog2 = new Blog('tom', '2015年4月5日');
echo $blog2->addBlog();

echo '<br />';

$blog3 = new Blog('mary', '2015年7月8日');
echo $blog3->addBlog();

此版本一开始只有 user,date 字段属性和 addBlog 方法,之后才添加了 title 字段属性和getTitle 方法。当我们添加 title,getTitle 时,我们发现,新增一个对象的细节功能时往往要修改其他地方,导致连锁反应,这是面向对象原则比较忌讳的,应该尽可能独立。

demo2.php

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
<?php
header('Content-Type:text/html; charset=utf-8');

class Blog{
private $user;
private $date;
private $title;

public function __construct($user, $date, $title){
$this->user = $user;
$this->date = $date;
$this->title = $title;
}

public function addBlog(){
return $this->user.'于'.$this->date.'发表了一篇无博文!';
}

public function getTitle(){
return $this->title;
}
}

$blog1 = new Blog('suse', '2015年3月2日','访问者模式');
echo $blog1->addBlog();
echo $blog1->getTitle();

echo '<br />';

$blog2 = new Blog('tom', '2015年4月5日', '观察者模式');
echo $blog2->addBlog();
echo $blog2->getTitle();

echo '<br />';

$blog3 = new Blog('mary', '2015年7月8日', '命令模式');
echo $blog3->addBlog();
echo $blog3->getTitle();
`

博客系统功能第二版:使用访问者模式新增获取博客等级

demo3.php

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
<?php
header('Content-Type:text/html; charset=utf-8');

class Blog{
private $user;
private $date;
private $title;

public function __get($key){
return $this->$key;
}

public function __construct($user, $date, $title){
$this->user = $user;
$this->date = $date;
$this->title = $title;
}

public function addBlog(){
return $this->user.'于'.$this->date.'发表了一篇无博文!';
}

public function getTitle(){
return $this->title;
}

// 创建一个新增功能的访问入口
public function visitLevel(LevelBlog $level_blog){
return $level_blog->getLevel($this);
}


// 创建一个新增功能的访问入口
public function visitNum(NumBlog $num_blog){
return $num_blog->getNum($this);
}
}

class LevelBlog{
private $level;

public function __construct($level){
$this->level = $level;
}

public function getLevel(Blog $blog){
return $blog->user.'的博客等级为:'.$this->level.'级';
}
}

class NumBlog{
private $num;

public function __construct($num){
$this->num = $num;
}

public function getNum(Blog $blog){
return $blog->user.'博文的总篇数为:'.$this->num.'篇';
}
}

$blog1 = new Blog('suse', '2015年3月2日','访问者模式');
echo $blog1->addBlog();
echo $blog1->getTitle();

echo '<br />';

$blog2 = new Blog('tom', '2015年4月5日', '观察者模式');
echo $blog2->addBlog();
echo $blog2->getTitle();

echo '<br />';

$blog3 = new Blog('mary', '2015年7月8日', '命令模式');
echo $blog3->addBlog();
echo $blog3->getTitle();
echo '<br />';
echo $blog3->visitLevel(new LevelBlog(5));
echo '<br />';
echo $blog3->visitNum(new NumBlog(100));

此版本解决了当 Blog 对象新增细节功能时,不需要对 Blog 进行修改,而只是增加一个访问者入口的方法,然后通过一个单独的类来封装算法。这样大大的提高了扩展性和维护性 ,从而不会对已存在的代码造成连锁反应。

博客系统功能第三版:合并了访问者

demo4.php

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
<?php
header('Content-Type:text/html; charset=utf-8');

class Blog{
private $user;
private $date;
private $title;

public function __get($key){
return $this->$key;
}

public function __construct($user, $date, $title){
$this->user = $user;
$this->date = $date;
$this->title = $title;
}

public function addBlog(){
return $this->user.'于'.$this->date.'发表了一篇无博文!';
}

public function getTitle(){
return $this->title;
}

// 创建一个新增功能的访问入口
public function visit(B $b){
return $b->get($this);
}

}

abstract class B{
abstract public function get(Blog $blog);
}

class LevelBlog extends B{
private $level;

public function __construct($level){
$this->level = $level;
}

public function get(Blog $blog){
return $blog->user.'的博客等级为:'.$this->level.'级';
}
}

class NumBlog extends B{
private $num;

public function __construct($num){
$this->num = $num;
}

public function get(Blog $blog){
return $blog->user.'博文的总篇数为:'.$this->num.'篇';
}
}

$blog1 = new Blog('suse', '2015年3月2日','访问者模式');
echo $blog1->addBlog();
echo $blog1->getTitle();

echo '<br />';

$blog2 = new Blog('tom', '2015年4月5日', '观察者模式');
echo $blog2->addBlog();
echo $blog2->getTitle();

echo '<br />';

$blog3 = new Blog('mary', '2015年7月8日', '命令模式');
echo $blog3->addBlog();
echo $blog3->getTitle();
echo '<br />';
echo $blog3->visit(new LevelBlog(5));
echo '<br />';
echo $blog3->visit(new NumBlog(100));

而 LevelBlog 和 NumBlog 如果功能单一,或者结构统一,可以合并到一个访问者入口里。但这样做,虽然节约了方法,但不够灵活。