首页 > 代码库 > Php设计模式(三):行为型模式part1

Php设计模式(三):行为型模式part1

原文详见:http://www.ucai.cn/blogdetail/7023?mid=1&f=5

可以在线运行查看效果哦!    

 

       在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。

一、什么是行为型模式?

行为型模式:

       就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。

二、行为型模式的种类

       大体上分为三个大类:常见模式、已知模式、深度模式

       常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态式、职责链模式、策略模式

       已知模式包括:备忘录模式

       深度模式包括:解释器模式 访问者模式

下面来介绍常见模式

  • 常见模式

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
154
155
<?php
 
/**
 * 优才网公开课示例代码
 *
 * 模板方法模式 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 = http://www.mamicode.com/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
266
<?php
/**
 * 优才网公开课示例代码
 *
 * 命令模式 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
121
122
<?php
 
/**
 * 优才网公开课示例代码
 *
 * 迭代器模式 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
<?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 "";
    }
}
 
//假设的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();

  

?
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
<?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();

《接下一篇》