PHP观察者模式
观察者模式是PHP设计模式中常用的一种,常用来解耦,一般情况下,会用到3个SPL接口:SplSubject
、SplObserver
、SplObjectStorage
,这三个接口的详细介绍可以参考PHP手册,我们这里只是使用,也有很多是自己实现观察者模式,没有用到SPL库的,这里暂且不谈。
先解释一下我理解里的观察者模式是怎么回事:顾名思义,像一个观察者一样,开始观察某个东西A的时候,当A发生某些改变之后,观察者可以做出相应的反应。比如我们常见的微信公众号,我们关注一个微信公众号的之后,每当这个公众号发布新文章的时候,微信服务器就会把这个文章推送给每一个关注的人,这就是微信服务器利用类似观察者模式,观察到公众号发布了新文章,就做出了相应的反应(把文章推给关注者)。
我们来看一下一个实例代码,这个是一个下订单,下单后记录日志、给用户和商城管理者发送邮件的过程,有点长,请耐心仔细看代码逻辑与注释:
xxxxxxxxxx
1
/**
2
* 观察者模式实例 Observer
3
*/
4
5
/**
6
*订单数据对象简单模拟
7
*这个是实际需要被观察的对象(Subject),
8
*但是我们将其独立,
9
*然后通过构造方法传入到我们模式中的Subject中,
10
*这样使具体业务更加独立
11
*/
12
class Order
13
{
14
//订单号
15
private $id = '';
16
17
//用户ID
18
private $userId = '';
19
20
//用户名
21
private $userName = '';
22
23
//价格
24
private $price = '';
25
26
//下单时间
27
private $orderTime = '';
28
29
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
30
public function __set($name, $value)
31
{
32
if (isset($this->$name)) {
33
$this->$name = $value;
34
}
35
}
36
37
//用魔术方法获取订单属性,方便扩展
38
public function __get($name)
39
{
40
if (isset($this->$name)) {
41
return $this->$name;
42
}
43
return "";
44
}
45
}
46
47
//被观察者, 负责监听类、维护观察者并在变化发生是通知观察者
48
class OrderSubject implements SplSubject
49
{
50
private $observers;
51
private $order;
52
53
public function __construct(Order $order)
54
{
55
$this->observers = new SplObjectStorage();
56
$this->order = $order;
57
}
58
59
//增加一个观察者
60
public function attach(SplObserver $observer)
61
{
62
$this->observers->attach($observer);
63
}
64
65
//移除一个观察者
66
public function detach(SplObserver $observer)
67
{
68
$this->observers->detach($observer);
69
}
70
71
//通知所有观察者
72
public function notify()
73
{
74
foreach ($this->observers as $observer) {
75
$observer->update($this);
76
}
77
}
78
79
//返回主体对象的具体实现,供观察者调用
80
public function getOrder()
81
{
82
return $this->order;
83
}
84
}
85
86
//观察者(事件类) 记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
87
class ActionLogObserver implements SplObserver
88
{
89
public function update(SplSubject $subject)
90
{
91
$order = $subject->getOrder();
92
//实际应用可能会写到日志文件中,这里直接输出
93
output("[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]");
94
}
95
}
96
97
//观察者(事件类) 给用户发送订单确认邮件 (UserMailObserver)
98
class UserMailObserver implements SplObserver
99
{
100
public function update(SplSubject $subject)
101
{
102
$order = $subject->getOrder();
103
//实际应用会调用邮件发送服务如sendmail,这里直接输出
104
output("Dear {$order->userName}: Your order {$order->id} was confirmed!");
105
}
106
}
107
108
//观察者(事件类) 给管理人员发订单处理通知邮件 (AdminMailObserver)
109
class AdminMailObserver implements SplObserver
110
{
111
public function update(SplSubject $subject)
112
{
113
$order = $subject->getOrder();
114
//实际应用会调用邮件发送服务如sendmail,这里直接输出
115
output("Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!");
116
}
117
}
118
119
function output($string)
120
{
121
echo $string . "n";
122
}
123
124
//假设的DB类,便于测试,实际会存入真实数据库
125
class FakeDB
126
{
127
public function save($data)
128
{
129
return true;
130
}
131
}
132
133
Client::test();
134
135
//客户端调用 注意
136
class Client
137
{
138
public static function test()
139
{
140
//初始化一个订单数据
141
$order = new Order();
142
$order->id = 123;
143
$order->userId = 321;
144
$order->userName = "God";
145
$order->price = 0.01;
146
$order->orderTime = time();
147
//监听order类
148
$subject = new OrderSubject($order);
149
//监听其他类
150
//$subject2 = new OrderSubject($else);
151
$actionLogObserver = new ActionLogObserver();
152
$userMailObserver = new UserMailObserver();
153
$adminMailObserver = new AdminMailObserver();
154
//增加一个观察者
155
$subject->attach($actionLogObserver);
156
$subject->attach($userMailObserver);
157
$subject->attach($adminMailObserver);
158
//向数据库保存订单
159
$db = new FakeDB();
160
$result = $db->save($order);
161
if ($result) {
162
//保存订单成功通知观察者
163
$subject->notify();
164
}
165
}
166
}
上面的代码与注释已经很详细了,其他就不在详述了。