在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。
一、什么是行为型模式?
行为型模式就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
1、常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、职责链模式、策略模式
2、 已知模式包括:备忘录模式
3、深度模式包括:解释器模式 访问者模式
下面来介绍常见模式
Ø常见模式
1、模版方法模式(Template):
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。 就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码。
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中。
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 模板方法模式 Template
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
class
Request {
public
$token
=
''
;
public
function
__construct() {
$this
->token =
'0c6b7289f5334ed2b697dd461eaf9812'
;
}
}
class
Response {
public
function
render(
$content
) {
output(sprintf(
'response-render: %s'
,
$content
));
}
public
function
redirect(
$uri
) {
output(sprintf(
'response-redirect: %s'
,
$uri
));
}
public
function
json(
$data
) {
output(sprintf(
'response-data: %s'
, json_encode(
$data
)));
}
}
//父类,抽象类
abstract
class
Controller{
//封装了输入输出
protected
$request
;
protected
$response
;
//返回数据
protected
$data
=
'data'
;
public
function
__construct(
$request
,
$response
){
$this
->request =
$request
;
$this
->response =
$response
;
}
//执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
public
final
function
execute(){
$this
->before();
if
(
$this
->valid()){
$this
->handleRequest();
}
$this
->after();
}
//定义hook method before,做一些具体请求的前置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
protected
function
before(){
}
//定义hook method valid,做请求的数据验证
//非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
protected
function
valid(){
return
true;
}
//定义hook method handleRequest,处理请求
//定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
abstract
function
handleRequest();
//定义hook method after,做一些请求的后置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
protected
function
after(){
$this
->response->render(
$this
->data);
}
}
//子类1,实现父类开放的具体算法
class
User
extends
Controller{
//覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
//因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
function
before(){
if
(
empty
(
$_SESSION
[
'auth'
])){
//没登录就直接跳转了,不再执行后续的操作
$this
->response->redirect(
"user/login.php"
);
}
}
//覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
function
valid(){
if
(isset(
$this
->request->token)){
return
true;
}
return
false;
}
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
function
handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
}
//这个类我们选择不覆盖after方法,使用默认处理方式
}
//子类2,实现父类开放的具体算法
class
Post
extends
Controller{
//这个类我们选择不覆盖before方法,使用默认处理方式
//这个类我们选择不覆盖valid方法,使用默认处理方式
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
function
handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
$this
->data =
array
(
'title'
=>
'ucai'
);
}
//覆盖after方法,使用json格式输出数据
function
after(){
$this
->response->json(
$this
->data);
}
}
class
Client {
public
static
function
test(){
$request
=
new
Request();
$response
=
new
Response();
//最终调用
$user
=
new
User(
$request
,
$response
);
$user
->execute();
//最终调用
$post
=
new
Post(
$request
,
$response
);
$post
->execute();
}
}
Client::test();
|
2、命令模式(Command) :
行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到 这个命令都会做出标准的敬礼动作。
好处:便于添加和修改行为,便于聚合多个命令。
弊端:造成过多具体的命令类。
应用场景:对要操作的对象,进行的相同操作。
代码实现
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
/**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
class
Document {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
showText() {
output(sprintf(
"showText: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-showText: %s"
,
$this
->name));
}
}
class
Graphics {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
drawCircle() {
output(sprintf(
"drawCircle: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-drawCircle: %s"
,
$this
->name));
}
}
class
Client {
public
static
function
test() {
$document
=
new
Document(
'A'
);
$graphics
=
new
Graphics(
'B'
);
$document
->showText();
$graphics
->drawCircle();
$document
->undo();
}
}
Client::test();
<?php
/**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
interface
Command {
public
function
execute();
public
function
undo();
}
class
Document
implements
Command {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
execute() {
output(sprintf(
"showText: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-showText: %s"
,
$this
->name));
}
}
class
Graphics
implements
Command {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
execute() {
output(sprintf(
"drawCircle: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-drawCircle: %s"
,
$this
->name));
}
}
class
Client {
public
static
function
test() {
$array
=
array
();
array_push
(
$array
,
new
Document(
'A'
));
array_push
(
$array
,
new
Document(
'B'
));
array_push
(
$array
,
new
Graphics(
'C'
));
array_push
(
$array
,
new
Graphics(
'D'
));
foreach
(
$array
as
$command
) {
$command
->execute();
}
$top
=
array_pop
(
$array
);
$top
->undo();
}
}
Client::test();
<?php
/**
* 优才网公开课示例代码
*
* 命令模式 Command
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
interface
Command {
public
function
execute();
public
function
undo();
}
class
Document {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
showText() {
output(sprintf(
"showText: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-showText: %s"
,
$this
->name));
}
}
class
Graphics {
private
$name
=
''
;
public
function
__construct(
$name
) {
$this
->name =
$name
;
}
public
function
drawCircle() {
output(sprintf(
"drawCircle: %s"
,
$this
->name));
}
public
function
undo() {
output(sprintf(
"undo-drawCircle: %s"
,
$this
->name));
}
}
class
DocumentCommand
implements
Command {
private
$obj
=
''
;
public
function
__construct(Document
$document
) {
$this
->obj =
$document
;
}
public
function
execute() {
$this
->obj->showText();
}
public
function
undo() {
$this
->obj->undo();
}
}
class
GraphicsCommand
implements
Command {
private
$obj
=
''
;
public
function
__construct(Graphics
$graphics
) {
$this
->obj =
$graphics
;
}
public
function
execute() {
$this
->obj->drawCircle();
}
public
function
undo() {
$this
->obj->undo();
}
}
class
Client {
public
static
function
test() {
$array
=
array
();
array_push
(
$array
,
new
DocumentCommand(
new
Document(
'A'
)));
array_push
(
$array
,
new
DocumentCommand(
new
Document(
'B'
)));
array_push
(
$array
,
new
GraphicsCommand(
new
Graphics(
'C'
)));
array_push
(
$array
,
new
GraphicsCommand(
new
Graphics(
'D'
)));
foreach
(
$array
as
$command
) {
$command
->execute();
}
$top
=
array_pop
(
$array
);
$top
->undo();
}
}
Client::test();
|
3、迭代器模式(Iterator):
访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一 样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。
好处:以不同方式遍历一个集合。
弊端:每次遍历都是整个集合,不能单独取出元素。
应用场景:需要操作集合里的全部元素。
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 迭代器模式 Iterator
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
class
RecordIterator
implements
Iterator{
private
$position
= 0;
//注意:被迭代对象属性是私有的
private
$records
=
array
();
public
function
__construct(Array
$records
) {
$this
->position = 0;
$this
->records =
$records
;
}
function
rewind
() {
$this
->position = 0;
}
function
current() {
return
$this
->records[
$this
->position];
}
function
key() {
return
$this
->position;
}
function
next() {
++
$this
->position;
}
function
valid() {
return
isset(
$this
->records[
$this
->position]);
}
}
class
PostListPager {
protected
$record
=
array
();
protected
$total
= 0;
protected
$page
= 0;
protected
$size
= 0;
public
function
__construct(
$category
,
$page
,
$size
) {
$this
->page =
$page
;
$this
->size =
$size
;
// query db
$total
= 28;
$this
->total =
$total
;
$record
=
array
(
0 =>
array
(
'id'
=>
'1'
),
1 =>
array
(
'id'
=>
'2'
),
2 =>
array
(
'id'
=>
'3'
),
3 =>
array
(
'id'
=>
'4'
),
);
//
$this
->record =
$record
;
}
public
function
getIterator() {
return
new
RecordIterator(
$this
->record);
}
public
function
getMaxPage() {
$max
=
intval
(
$this
->total /
$this
->size);
return
$max
;
}
public
function
getPrevPage() {
return
max(
$this
->page - 1, 1);
}
public
function
getNextPage() {
return
min(
$this
->page + 1,
$this
->getMaxPage());
}
}
class
Client {
public
static
function
test(){
$pager
=
new
PostListPager(1, 2, 4);
foreach
(
$pager
->getIterator()
as
$key
=>
$val
) {
output(sprintf(
'Key[%d],Val[%s]'
,
$key
, json_encode(
$val
)));
}
output(sprintf(
'MaxPage[%d]'
,
$pager
->getMaxPage()));
output(sprintf(
'Prev[%d]'
,
$pager
->getPrevPage()));
output(sprintf(
'Next[%d]'
,
$pager
->getNextPage()));
$iterator
=
$pager
->getIterator();
while
(
$iterator
->valid()){
print_r(
$iterator
->current());
$iterator
->next();
}
$iterator
->
rewind
();
}
}
Client::test();
|
4、观察者模式(Observer):
又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察 者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要 是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一 样的。
好处:广播式通信,范围大一呼百应,便于操作一个组团,“公有制”。
弊端:不能单独操作组团里的个体,不能实行按需分配。
应用场景:操作多个对象,并操作相同。
代码实现
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
/**
* 优才网公开课示例代码
*
* 观察者模式 Observer
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class
Order{
//订单号
private
$id
=
''
;
//用户ID
private
$userId
=
''
;
//用户名
private
$userName
=
''
;
//价格
private
$price
=
''
;
//下单时间
private
$orderTime
=
''
;
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
public
function
__set(
$name
,
$value
){
if
(isset(
$this
->
$name
)){
$this
->
$name
=
$value
;
}
}
//获取订单属性
public
function
__get(
$name
){
if
(isset(
$this
->
$name
)){
return
$this
->
$name
;
}
return
""
;
}
}
//假设的DB类,便于测试,实际会存入真实数据库
class
FakeDB{
public
function
save(
$data
){
return
true;
}
}
class
Client {
public
static
function
test() {
//初始化一个订单数据
$order
=
new
Order();
$order
->id = 1001;
$order
->userId = 9527;
$order
->userName =
"God"
;
$order
->price = 20.0;
$order
->orderTime = time();
//向数据库保存订单
$db
=
new
FakeDB();
$result
=
$db
->save(
$order
);
if
(
$result
){
//实际应用可能会写到日志文件中,这里直接输出
output(
"[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]"
);
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output(
"Dear {$order->userName}: Your order {$order->id} was confirmed!"
);
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output(
"Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!"
);
}
}
}
Client::test();
<?php
/**
* 优才网公开课示例代码
*
* 观察者模式 Observer
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class
Order{
//订单号
private
$id
=
''
;
//用户ID
private
$userId
=
''
;
//用户名
private
$userName
=
''
;
//价格
private
$price
=
''
;
//下单时间
private
$orderTime
=
''
;
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
public
function
__set(
$name
,
$value
){
if
(isset(
$this
->
$name
)){
$this
->
$name
=
$value
;
}
}
//获取订单属性
public
function
__get(
$name
){
if
(isset(
$this
->
$name
)){
return
$this
->
$name
;
}
return
""
;
}
}
//被观察者, 负责维护观察者并在变化发生是通知观察者
class
OrderSubject
implements
SplSubject {
private
$observers
;
private
$order
;
public
function
__construct(Order
$order
) {
$this
->observers =
new
SplObjectStorage();
$this
->order =
$order
;
}
//增加一个观察者
public
function
attach(SplObserver
$observer
) {
$this
->observers->attach(
$observer
);
}
//移除一个观察者
public
function
detach(SplObserver
$observer
) {
$this
->observers->detach(
$observer
);
}
//通知所有观察者
public
function
notify() {
foreach
(
$this
->observers
as
$observer
) {
$observer
->update(
$this
);
}
}
//返回主体对象的具体实现,供观察者调用
public
function
getOrder() {
return
$this
->order;
}
}
//记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
class
ActionLogObserver
implements
SplObserver{
public
function
update(SplSubject
$subject
) {
$order
=
$subject
->getOrder();
//实际应用可能会写到日志文件中,这里直接输出
output(
"[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]"
);
}
}
//给用户发送订单确认邮件 (UserMailObserver)
class
UserMailObserver
implements
SplObserver{
public
function
update(SplSubject
$subject
) {
$order
=
$subject
->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output(
"Dear {$order->userName}: Your order {$order->id} was confirmed!"
);
}
}
//给管理人员发订单处理通知邮件 (AdminMailObserver)
class
AdminMailObserver
implements
SplObserver{
public
function
update(SplSubject
$subject
) {
$order
=
$subject
->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output(
"Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!"
);
}
}
//假设的DB类,便于测试,实际会存入真实数据库
class
FakeDB{
public
function
save(
$data
){
return
true;
}
}
class
Client {
public
static
function
test() {
//初始化一个订单数据
$order
=
new
Order();
$order
->id = 1001;
$order
->userId = 9527;
$order
->userName =
"God"
;
$order
->price = 20.0;
$order
->orderTime = time();
//绑定观察者
$subject
=
new
OrderSubject(
$order
);
$actionLogObserver
=
new
ActionLogObserver();
$userMailObserver
=
new
UserMailObserver();
$adminMailObserver
=
new
AdminMailObserver();
$subject
->attach(
$actionLogObserver
);
$subject
->attach(
$userMailObserver
);
$subject
->attach(
$adminMailObserver
);
//向数据库保存订单
$db
=
new
FakeDB();
$result
=
$db
->save(
$order
);
if
(
$result
){
//通知观察者
$subject
->notify();
}
}
}
Client::test();
|
5、中介者模式(Mediator):
用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引 用。
类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
好处:简化了对象之间的关系,减少子类的生成。
弊端:中介对象可能变得非常复杂,系统难以维护。
应用场景:不需要显示地建立交互
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 中介者模式 Mediator
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
abstract
class
Mediator {
// 中介者角色
abstract
public
function
send(
$message
,
$colleague
);
}
abstract
class
Colleague {
// 抽象对象
private
$_mediator
= null;
public
function
__construct(
$mediator
) {
$this
->_mediator =
$mediator
;
}
public
function
send(
$message
) {
$this
->_mediator->send(
$message
,
$this
);
}
abstract
public
function
notify(
$message
);
}
class
ConcreteMediator
extends
Mediator {
// 具体中介者角色
private
$_colleague1
= null;
private
$_colleague2
= null;
public
function
send(
$message
,
$colleague
) {
if
(
$colleague
==
$this
->_colleague1) {
$this
->_colleague1->notify(
$message
);
}
else
{
$this
->_colleague2->notify(
$message
);
}
}
public
function
set(
$colleague1
,
$colleague2
) {
$this
->_colleague1 =
$colleague1
;
$this
->_colleague2 =
$colleague2
;
}
}
class
Colleague1
extends
Colleague {
// 具体对象角色
public
function
notify(
$message
) {
output(sprintf(
'Colleague-1: %s'
,
$message
));
}
}
class
Colleague2
extends
Colleague {
// 具体对象角色
public
function
notify(
$message
) {
output(sprintf(
'Colleague-2: %s'
,
$message
));
}
}
class
Client {
public
static
function
test(){
// client
$objMediator
=
new
ConcreteMediator();
$objC1
=
new
Colleague1(
$objMediator
);
$objC2
=
new
Colleague2(
$objMediator
);
$objMediator
->set(
$objC1
,
$objC2
);
$objC1
->send(
"to c2 from c1"
);
$objC2
->send(
"to c1 from c2"
);
}
}
Client::test();
|
6、状态模式(State) :
对象在不同状态下表现出不同的行为。就像女朋友一样,高兴了牵你的 手,不高兴了遛狗。在两种状态下变现出不同的行为。
好处:避免if语句实用,方便增加新状态,封装了状态转换规则。
弊端:增加系统类和对象的数量。
应用场景:用于对象的不同功能的转换。
代码实现
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
|
/**
* 优才网公开课示例代码
*
* 状态模式 State
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
abstract
class
ILift {
//电梯的四个状态
const
OPENING_STATE = 1;
//门敞状态
const
CLOSING_STATE = 2;
//门闭状态
const
RUNNING_STATE = 3;
//运行状态
const
STOPPING_STATE = 4;
//停止状态;
//设置电梯的状态
public
abstract
function
setState(
$state
);
//首先电梯门开启动作
public
abstract
function
open();
//电梯门有开启,那当然也就有关闭了
public
abstract
function
close();
//电梯要能上能下,跑起来
public
abstract
function
run();
//电梯还要能停下来
public
abstract
function
stop();
}
/**
* 电梯的实现类
*/
class
Lift
extends
ILift {
private
$state
;
public
function
setState(
$state
) {
$this
->state =
$state
;
}
//电梯门关闭
public
function
close() {
//电梯在什么状态下才能关闭
switch
(
$this
->state) {
case
ILift::OPENING_STATE:
//如果是则可以关门,同时修改电梯状态
$this
->setState(ILift::CLOSING_STATE);
break
;
case
ILift::CLOSING_STATE:
//如果电梯就是关门状态,则什么都不做
//do nothing;
return
;
break
;
case
ILift::RUNNING_STATE:
//如果是正在运行,门本来就是关闭的,也说明都不做
//do nothing;
return
;
break
;
case
ILift::STOPPING_STATE:
//如果是停止状态,本也是关闭的,什么也不做
//do nothing;
return
;
break
;
}
output(
'Lift colse'
);
}
//电梯门开启
public
function
open() {
//电梯在什么状态才能开启
switch
(
$this
->state){
case
ILift::OPENING_STATE:
//如果已经在门敞状态,则什么都不做
//do nothing;
return
;
break
;
case
ILift::CLOSING_STATE:
//如是电梯时关闭状态,则可以开启
$this
->setState(ILift::OPENING_STATE);
break
;
case
ILift::RUNNING_STATE:
//正在运行状态,则不能开门,什么都不做
//do nothing;
return
;
break
;
case
ILift::STOPPING_STATE:
//停止状态,淡然要开门了
$this
->setState(ILift::OPENING_STATE);
break
;
}
output(
'Lift open'
);
}
///电梯开始跑起来
public
function
run() {
switch
(
$this
->state){
case
ILift::OPENING_STATE:
//如果已经在门敞状态,则不你能运行,什么都不做
//do nothing;
return
;
break
;
case
ILift::CLOSING_STATE:
//如是电梯时关闭状态,则可以运行
$this
->setState(ILift::RUNNING_STATE);
break
;
case
ILift::RUNNING_STATE:
//正在运行状态,则什么都不做
//do nothing;
return
;
break
;
case
ILift::STOPPING_STATE:
//停止状态,可以运行
$this
->setState(ILift::RUNNING_STATE);
}
output(
'Lift run'
);
}
//电梯停止
public
function
stop() {
switch
(
$this
->state){
case
ILift::OPENING_STATE:
//如果已经在门敞状态,那肯定要先停下来的,什么都不做
//do nothing;
return
;
break
;
case
ILift::CLOSING_STATE:
//如是电梯时关闭状态,则当然可以停止了
$this
->setState(ILift::CLOSING_STATE);
break
;
case
ILift::RUNNING_STATE:
//正在运行状态,有运行当然那也就有停止了
$this
->setState(ILift::CLOSING_STATE);
break
;
case
ILift::STOPPING_STATE:
//停止状态,什么都不做
//do nothing;
return
;
break
;
}
output(
'Lift stop'
);
}
}
class
Client {
public
static
function
test() {
$lift
=
new
Lift();
//电梯的初始条件应该是停止状态
$lift
->setState(ILift::STOPPING_STATE);
//首先是电梯门开启,人进去
$lift
->open();
//然后电梯门关闭
$lift
->close();
//再然后,电梯跑起来,向上或者向下
$lift
->run();
//最后到达目的地,电梯挺下来
$lift
->stop();
}
}
Client::test();
<?php
/**
* 优才网公开课示例代码
*
* 状态模式 State
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
/**
*
* 定义一个电梯的接口
*/
abstract
class
LiftState{
//定义一个环境角色,也就是封装状态的变换引起的功能变化
protected
$_context
;
public
function
setContext(Context
$context
){
$this
->_context =
$context
;
}
//首先电梯门开启动作
public
abstract
function
open();
//电梯门有开启,那当然也就有关闭了
public
abstract
function
close();
//电梯要能上能下,跑起来
public
abstract
function
run();
//电梯还要能停下来,停不下来那就扯淡了
public
abstract
function
stop();
}
/**
* 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
*/
class
Context {
//定义出所有的电梯状态
static
$openningState
= null;
static
$closeingState
= null;
static
$runningState
= null;
static
$stoppingState
= null;
public
function
__construct() {
self::
$openningState
=
new
OpenningState();
self::
$closeingState
=
new
ClosingState();
self::
$runningState
=
new
RunningState();
self::
$stoppingState
=
new
StoppingState();
}
//定一个当前电梯状态
private
$_liftState
;
public
function
getLiftState() {
return
$this
->_liftState;
}
public
function
setLiftState(
$liftState
) {
$this
->_liftState =
$liftState
;
//把当前的环境通知到各个实现类中
$this
->_liftState->setContext(
$this
);
}
public
function
open(){
$this
->_liftState->open();
}
public
function
close(){
$this
->_liftState->close();
}
public
function
run(){
$this
->_liftState->run();
}
public
function
stop(){
$this
->_liftState->stop();
}
}
/**
* 在电梯门开启的状态下能做什么事情
*/
class
OpenningState
extends
LiftState {
/**
* 开启当然可以关闭了,我就想测试一下电梯门开关功能
*
*/
public
function
close() {
//状态修改
$this
->_context->setLiftState(Context::
$closeingState
);
//动作委托为CloseState来执行
$this
->_context->getLiftState()->close();
}
//打开电梯门
public
function
open() {
output(
'lift open...'
);
}
//门开着电梯就想跑,这电梯,吓死你!
public
function
run() {
//do nothing;
}
//开门还不停止?
public
function
stop() {
//do nothing;
}
}
/**
* 电梯门关闭以后,电梯可以做哪些事情
*/
class
ClosingState
extends
LiftState {
//电梯门关闭,这是关闭状态要实现的动作
public
function
close() {
output(
'lift close...'
);
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
public
function
open() {
$this
->_context->setLiftState(Context::
$openningState
);
//置为门敞状态
$this
->_context->getLiftState()->open();
}
//电梯门关了就跑,这是再正常不过了
public
function
run() {
$this
->_context->setLiftState(Context::
$runningState
);
//设置为运行状态;
$this
->_context->getLiftState()->run();
}
//电梯门关着,我就不按楼层
public
function
stop() {
$this
->_context->setLiftState(Context::
$stoppingState
);
//设置为停止状态;
$this
->_context->getLiftState()->stop();
}
}
/**
* 电梯在运行状态下能做哪些动作
*/
class
RunningState
extends
LiftState {
//电梯门关闭?这是肯定了
public
function
close() {
//do nothing
}
//运行的时候开电梯门?你疯了!电梯不会给你开的
public
function
open() {
//do nothing
}
//这是在运行状态下要实现的方法
public
function
run() {
output(
'lift run...'
);
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
public
function
stop() {
$this
->_context->setLiftState(Context::
$stoppingState
);
//环境设置为停止状态;
$this
->_context->getLiftState()->stop();
}
}
/**
* 在停止状态下能做什么事情
*/
class
StoppingState
extends
LiftState {
//停止状态关门?电梯门本来就是关着的!
public
function
close() {
//do nothing;
}
//停止状态,开门,那是要的!
public
function
open() {
$this
->_context->setLiftState(Context::
$openningState
);
$this
->_context->getLiftState()->open();
}
//停止状态再跑起来,正常的很
public
function
run() {
$this
->_context->setLiftState(Context::
$runningState
);
$this
->_context->getLiftState()->run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
public
function
stop() {
output(
'lift stop...'
);
}
}
/**
* 模拟电梯的动作
*/
class
Client {
public
static
function
test() {
$context
=
new
Context();
$context
->setLiftState(
new
ClosingState());
$context
->open();
$context
->close();
$context
->run();
$context
->stop();
}
}
Client::test();
|
7、职责链模式 (Chainof Responsibility):
多个对象有机会处理请求,为请求发送者和接收者解耦。就像银行里 的取款机,不管那一台都可以取到钱。
好处:简单化对象隐藏链结构,便于添加新职责节点。
弊端:请求可能没有接受者,或者被多个接收者调用,性能降低。
应用场景:处理多种请求。
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 职责链模式 Chain of Responsibility
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
/**
* 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。
如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。
如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字,
如果请假天数超过10天,是任何人都不能批准的。
*/
/**
* 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)
*
*/
abstract
class
Handler
{
protected
$_handler
= null;
protected
$_handlerName
= null;
public
function
setSuccessor(
$handler
)
{
$this
->_handler =
$handler
;
}
protected
function
_success(
$request
)
{
output(sprintf(
"%s's request was passed"
,
$request
->getName()));
return
true;
}
abstract
function
handleRequest(
$request
);
}
/**
* 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。
*
*/
class
ConcreteHandlerLeader
extends
Handler
{
function
__construct(
$handlerName
){
$this
->_handlerName =
$handlerName
;
}
public
function
handleRequest(
$request
)
{
if
(
$request
->getDay() < 0.5) {
output(sprintf(
'%s was told'
,
$this
->_handlerName));
// 已经跟leader招呼了
return
$this
->_success(
$request
);
}
if
(
$this
->_handler
instanceof
Handler) {
return
$this
->_handler->handleRequest(
$request
);
}
}
}
/**
* Manager
*
*/
class
ConcreteHandlerManager
extends
Handler
{
function
__construct(
$handlerName
){
$this
->_handlerName =
$handlerName
;
}
public
function
handleRequest(
$request
)
{
if
(0.5 <=
$request
->getDay() &&
$request
->getDay()<=3) {
output(sprintf(
'%s signed'
,
$this
->_handlerName));
// 部门经理签字
return
$this
->_success(
$request
);
}
if
(
$this
->_handler
instanceof
Handler) {
return
$this
->_handler->handleRequest(
$request
);
}
}
}
class
ConcreteHandlerGeneralManager
extends
Handler
{
function
__construct(
$handlerName
){
$this
->_handlerName =
$handlerName
;
}
public
function
handleRequest(
$request
)
{
if
(3 <
$request
->getDay() &&
$request
->getDay() < 10){
output(sprintf(
'%s signed'
,
$this
->_handlerName));
// 总经理签字
return
$this
->_success(
$request
);
}
if
(
$this
->_handler
instanceof
Handler) {
return
$this
->_handler->handleRequest(
$request
);
}
else
{
output(sprintf(
'no one can approve request more than 10 days'
));
}
}
}
/**
* 请假申请
*
*/
class
Request
{
private
$_name
;
private
$_day
;
private
$_reason
;
function
__construct(
$name
=
''
,
$day
= 0,
$reason
=
''
){
$this
->_name =
$name
;
$this
->_day =
$day
;
$this
->_reason =
$reason
;
}
public
function
setName(
$name
){
$this
->_name =
$name
;
}
public
function
getName(){
return
$this
->_name;
}
public
function
setDay(
$day
){
$this
->_day =
$day
;
}
public
function
getDay(){
return
$this
->_day ;
}
public
function
setReason(
$reason
){
$this
->_reason =
$reason
;
}
public
function
getReason( ){
return
$this
->_reason;
}
}
class
Client {
public
static
function
test(){
$leader
=
new
ConcreteHandlerLeader(
'leader'
);
$manager
=
new
ConcreteHandlerManager(
'manager'
);
$generalManager
=
new
ConcreteHandlerGeneralManager(
'generalManager'
);
//请求实例
$request
=
new
Request(
'ucai'
,4,
'休息'
);
$leader
->setSuccessor(
$manager
);
$manager
->setSuccessor(
$generalManager
);
$result
=
$leader
->handleRequest(
$request
);
}
}
Client::test();
|
8、策略模式(Strategy):
定义一系列算法,把每一个算法封装起来,并且使它们可相互替换。 就像篮球队里的球员,场上的和场下休息的。教练可以让场上的下来,也 可以让场下的上阵。
好处:定义可重用的一系列算法和行为,并且消除了if else语句。
弊端:调用端必须知道所有策略类。
应用场景:用于对象间的替换。
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 策略模式 Strategy
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
//策略基类接口
interface
IStrategy {
public
function
OnTheWay();
}
class
WalkStrategy
implements
IStrategy {
public
function
OnTheWay() {
output(
'在路上步行'
);
}
}
class
RideBickStrategy
implements
IStrategy {
public
function
OnTheWay() {
output(
'在路上骑自行车'
);
}
}
class
CarStrategy
implements
IStrategy {
public
function
OnTheWay() {
output(
'在路上开车'
);
}
}
//选择策略类Context
class
Context {
public
function
find(
$strategy
) {
$strategy
->OnTheWay();
}
}
class
Client {
public
static
function
test(){
$travel
=
new
Context();
$travel
->find(
new
WalkStrategy());
$travel
->find(
new
RideBickStrategy());
$travel
->find(
new
CarStrategy());
}
}
Client::test();
|
Ø已知模式
1、备忘录模式(Memento):
保存对象在一时刻的状态。亲,还记得“老师来了记得叫我一下”的 同桌的他吗?
好处:给用户提供了一种可以恢复状态的机制。
弊端:消耗资源。
应用场景:用于需要保存的数据。
代码实现
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
|
/**
* 优才网公开课示例代码
*
* 备忘录模式 Memento
*
* @author 优才网全栈工程师教研组
* @see http://www.ucai.cn
*/
function
output(
$string
) {
echo
$string
.
"n"
;
}
class
Originator {
// 发起人(Originator)角色
private
$_state
;
public
function
__construct() {
$this
->_state =
''
;
}
public
function
createMemento() {
// 创建备忘录
return
new
Memento(
$this
->_state);
}
public
function
restoreMemento(Memento
$memento
) {
// 将发起人恢复到备忘录对象记录的状态上
$this
->_state =
$memento
->getState();
}
public
function
setState(
$state
) {
$this
->_state =
$state
; }
public
function
getState() {
return
$this
->_state; }
public
function
showState() {
output(
$this
->_state);
}
}
class
Memento {
// 备忘录(Memento)角色
private
$_state
;
public
function
__construct(
$state
) {
$this
->setState(
$state
);
}
public
function
getState() {
return
$this
->_state; }
public
function
setState(
$state
) {
$this
->_state =
$state
;}
}
class
Caretaker {
// 负责人(Caretaker)角色
private
$_memento
;
public
function
getMemento() {
return
$this
->_memento; }
public
function
setMemento(Memento
$memento
) {
$this
->_memento =
$memento
; }
}
class
Client {
public
static
function
test(){
$org
=
new
Originator();
$org
->setState(
'open'
);
$org
->showState();
/* 创建备忘 */
$memento
=
$org
->createMemento();
/* 通过Caretaker保存此备忘 */
$caretaker
=
new
Caretaker();
$caretaker
->setMemento(
$memento
);
/* 改变目标对象的状态 */
$org
->setState(
'close'
);
$org
->showState();
/* 还原操作 */
$org
->restoreMemento(
$caretaker
->getMemento());
$org
->showState();
}
}
Client::test();
return
;
try
{
$db
->beginTransaction();
$succ
=
$db
->
exec
(
$sql_1
);
if
(!
$succ
) {
throw
new
Exception(
'SQL 1 update failed'
);
}
$succ
=
$db
->
exec
(
$sql_2
);
if
(!
$succ
) {
throw
new
Exception(
'SQL 2 update failed'
);
}
$succ
=
$db
->
exec
(
$sql_3
);
if
(!
$succ
) {
throw
new
Exception(
'SQL 3 update failed'
);
}
$db
->commit();
}
catch
(Exception
$exp
) {
$db
->rollBack();
}
|
Ø深度模式
1、解释器模式(Interpreter):
定义语言的文法,并建立一个解释器解释该语言中的句子。每个用过 字典的童鞋都懂滴。
好处:可扩展性比较好,灵活性大
弊端:可能难以维护复杂的文法
应用场景:用于成对或者一对多的需求中
2、访问者模式(Visitor):
封装某些用于作用于某种数据结构中各元素的操作,可以在不改变数 据结构的前提下定义作用于这些元素的新操作。如银行排号机。
好处:将相关的事物集中到一个访问者对象中。
弊端:增加新数据结构很困难。
应用场景:排队,排号。
三、总结
本篇介绍了行为型模式,行为模式涉及到算法和对象职责间的分配,行为类
模式采用继承机制在类间分派行为,TemplateMethod和Interpreter是类行为
模式。行为对象模式使用对象复合而不是继承,一些行为对象模式描述了一组相
互对等的对象如何相互协作以完成其中任何一个对象都单独无法完成的任务,如
Mediator在对象间引入一个mediator对象提供了松耦合所需的间接性;
Chain of Responsibility提供了更松的耦合,它通过一条候选对象链隐式的向一
个对象发松请求,可以运行时刻决定哪些候选者参与到链中;Observer定义并保
持了对象间的依赖关系;其它的行为对象模式常将行为封装封装在一个对象中,
并将请求指派给它,Strategy模式将算法封装在对象中,这样可以方面的改变和
指定一个对象所使用的算法;Command模式将请求封装在对象中,这样它就可
以作为参数来传递,已可以存储在历史列表中或以其它方式使用;State模式封
装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行
为;Visitor模式封装分布于多个类之间的行为;而Iterator模式则抽象了访问和
遍历一个集合中对象的方式。
转载自: http://www.ucai.cn/blogdetail/7022?action=gt&mid=1