php设计模式,四:行为型模式

一、什么是行为型模式?

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

二、行为型模式的种类

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

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

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

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

下面来介绍常见模式

Ø 常见模式

1、模版方法模式(Template):

定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。 就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。

好处:扩展性好,封装不变的代码,扩展可变的代码。

弊端:灵活性差,不能改变骨架部分。

应用场景:一类或一组具有共性的事物中。

代码实现

  1     /**
  2      *
  3      * 模板方法模式 Template
  4      *
  5      */
  6          
  7     function output($string) {
  8         echo    $string . "n";
  9     }
 10          
 11     class Request {
 12          
 13         public $token = '';
 14          
 15         public function __construct() {
 16             $this->token    = '0c6b7289f5334ed2b697dd461eaf9812';
 17         }
 18          
 19     }
 20          
 21     class Response {
 22          
 23         public function render($content) {
 24             output(sprintf('response-render: %s', $content));
 25         }
 26          
 27         public function redirect($uri) {
 28             output(sprintf('response-redirect: %s', $uri));
 29         }
 30          
 31         public function json($data) {
 32             output(sprintf('response-data: %s', json_encode($data)));
 33         }
 34          
 35     }
 36          
 37      //父类,抽象类
 38     abstract class Controller{
 39         //封装了输入输出
 40         protected $request;
 41         protected $response;
 42          
 43         //返回数据
 44         protected $data = 'data';
 45          
 46         public function __construct($request, $response){
 47             $this->request = $request;
 48             $this->response = $response;
 49         }
 50          
 51         //执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
 52         public final function execute(){
 53             $this->before();
 54             if ($this->valid()){
 55                 $this->handleRequest();
 56             }
 57             $this->after();
 58         }
 59          
 60         //定义hook method before,做一些具体请求的前置处理
 61         //非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
 62         protected function before(){
 63          
 64         }
 65          
 66         //定义hook method valid,做请求的数据验证
 67         //非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
 68         protected function valid(){
 69             return true;
 70         }
 71          
 72         //定义hook method handleRequest,处理请求
 73         //定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
 74         abstract function handleRequest();
 75          
 76         //定义hook method after,做一些请求的后置处理
 77         //非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
 78         protected function after(){
 79             $this->response->render($this->data);
 80         }
 81     }
 82          
 83     //子类1,实现父类开放的具体算法
 84     class User extends Controller{
 85         //覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
 86         //因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
 87         function before(){
 88             if (empty($_SESSION['auth'])){
 89                 //没登录就直接跳转了,不再执行后续的操作
 90                 $this->response->redirect("user/login.php");
 91             }
 92         }
 93          
 94         //覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
 95         function valid(){
 96             if (isset($this->request->token)){
 97                 return true;
 98             }
 99             return false;
100         }
101          
102         //覆盖handleRequest方法,必选,以为父类中声明了abstract了
103         function handleRequest(){
104             //做具体处理,一般根据参数执行不同的业务逻辑
105         }
106          
107         //这个类我们选择不覆盖after方法,使用默认处理方式
108     }
109          
110     //子类2,实现父类开放的具体算法
111     class Post extends Controller{
112         //这个类我们选择不覆盖before方法,使用默认处理方式
113          
114         //这个类我们选择不覆盖valid方法,使用默认处理方式
115          
116         //覆盖handleRequest方法,必选,以为父类中声明了abstract了
117         function handleRequest(){
118             //做具体处理,一般根据参数执行不同的业务逻辑
119             $this->data = array('title' => 'ucai');
120         }
121          
122         //覆盖after方法,使用json格式输出数据
123         function after(){
124             $this->response->json($this->data);
125         }
126     }
127          
128          
129          
130     class Client {  
131                
132         public static function test(){  
133          
134             $request        = new Request();
135             $response       = new Response();
136          
137             //最终调用
138             $user = new User($request, $response);
139             $user->execute();
140          
141          
142             //最终调用
143             $post = new Post($request, $response);
144             $post->execute();
145          
146         }  
147                
148     }  
149            
150     Client::test();

2、命令模式(Command) :

行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到 这个命令都会做出标准的敬礼动作。

好处:便于添加和修改行为,便于聚合多个命令。

弊端:造成过多具体的命令类。

应用场景:对要操作的对象,进行的相同操作。

代码实现

  1     /**
  2      *
  3      * 命令模式 Command
  4      *
  5      */
  6           
  7     function output($string) {
  8         echo    $string . "n";
  9     }
 10           
 11     class Document {
 12           
 13         private $name = '';
 14           
 15         public function __construct($name) {
 16             $this->name = $name;
 17         }
 18           
 19         public function showText() {
 20             output(sprintf("showText: %s", $this->name));
 21         }
 22           
 23         public function undo() {
 24             output(sprintf("undo-showText: %s", $this->name));
 25         }
 26           
 27     }
 28           
 29     class Graphics {
 30           
 31         private $name = '';
 32           
 33         public function __construct($name) {
 34             $this->name = $name;
 35         }
 36           
 37         public function drawCircle() {
 38             output(sprintf("drawCircle: %s", $this->name));
 39         }
 40           
 41         public function undo() {
 42             output(sprintf("undo-drawCircle: %s", $this->name));
 43         }
 44           
 45     }
 46           
 47     class Client {
 48               
 49         public static function test() {
 50           
 51             $document       = new Document('A');
 52             $graphics       = new Graphics('B');
 53           
 54             $document->showText();
 55             $graphics->drawCircle();
 56           
 57             $document->undo();
 58           
 59         }
 60           
 61     }
 62           
 63     Client::test();
 64           
 65           
 66     <?php
 67           
 68     /*
 69     *
 70      * 命令模式 Command
 71      *
 72      */
 73           
 74     function output($string) {
 75         echo    $string . "n";
 76     }
 77           
 78     interface Command {
 79         public function execute();
 80         public function undo();
 81     }
 82           
 83     class Document implements Command {
 84           
 85         private $name = '';
 86           
 87         public function __construct($name) {
 88             $this->name = $name;
 89         }
 90           
 91         public function execute() {
 92             output(sprintf("showText: %s", $this->name));
 93         }
 94           
 95         public function undo() {
 96             output(sprintf("undo-showText: %s", $this->name));
 97         }
 98           
 99     }
100           
101     class Graphics implements Command {
102           
103         private $name = '';
104           
105         public function __construct($name) {
106             $this->name = $name;
107         }
108           
109         public function execute() {
110             output(sprintf("drawCircle: %s", $this->name));
111         }
112           
113         public function undo() {
114             output(sprintf("undo-drawCircle: %s", $this->name));
115         }
116           
117     }
118           
119     class Client {
120               
121         public static function test() {
122           
123             $array          = array();
124           
125             array_push($array, new Document('A'));
126             array_push($array, new Document('B'));
127             array_push($array, new Graphics('C'));
128             array_push($array, new Graphics('D'));
129                   
130             foreach ($array as $command) {
131                 $command->execute();
132             }
133           
134             $top            = array_pop($array);
135             $top->undo();
136           
137         }
138           
139     }
140           
141     Client::test();
142           
143           
144     <?php
145           
146     /**
147      *
148      * 命令模式 Command
149      *
150      */
151           
152     function output($string) {
153         echo    $string . "n";
154     }
155           
156     interface Command {
157         public function execute();
158         public function undo();
159     }
160           
161     class Document {
162           
163         private $name = '';
164           
165         public function __construct($name) {
166             $this->name = $name;
167         }
168           
169         public function showText() {
170             output(sprintf("showText: %s", $this->name));
171         }
172           
173         public function undo() {
174             output(sprintf("undo-showText: %s", $this->name));
175         }
176           
177     }
178           
179     class Graphics {
180           
181         private $name = '';
182           
183         public function __construct($name) {
184             $this->name = $name;
185         }
186           
187         public function drawCircle() {
188             output(sprintf("drawCircle: %s", $this->name));
189         }
190           
191         public function undo() {
192             output(sprintf("undo-drawCircle: %s", $this->name));
193         }
194           
195     }
196           
197     class DocumentCommand implements Command {
198           
199         private $obj = '';
200           
201         public function __construct(Document $document) {
202             $this->obj = $document;
203         }
204           
205         public function execute() {
206             $this->obj->showText();
207         }
208           
209         public function undo() {
210             $this->obj->undo();
211         }
212           
213     }
214           
215     class GraphicsCommand implements Command {
216           
217         private $obj = '';
218           
219         public function __construct(Graphics $graphics) {
220             $this->obj = $graphics;
221         }
222           
223         public function execute() {
224             $this->obj->drawCircle();
225         }
226           
227         public function undo() {
228             $this->obj->undo();
229         }
230           
231     }
232           
233           
234     class Client {
235               
236         public static function test() {
237           
238             $array          = array();
239           
240             array_push($array, new DocumentCommand(new Document('A')));
241             array_push($array, new DocumentCommand(new Document('B')));
242             array_push($array, new GraphicsCommand(new Graphics('C')));
243             array_push($array, new GraphicsCommand(new Graphics('D')));
244                   
245             foreach ($array as $command) {
246                 $command->execute();
247             }
248           
249             $top            = array_pop($array);
250             $top->undo();
251           
252         }
253           
254     }
255           
256     Client::test();

3、迭代器模式(Iterator):

访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一 样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。

好处:以不同方式遍历一个集合。

弊端:每次遍历都是整个集合,不能单独取出元素。

应用场景:需要操作集合里的全部元素。

代码实现

  1     /**
  2      *
  3      * 迭代器模式 Iterator
  4      *
  5      */
  6           
  7     function output($string) {
  8         echo    $string . "n";
  9     }
 10           
 11     class RecordIterator implements Iterator{
 12           
 13         private $position = 0;
 14           
 15         //注意:被迭代对象属性是私有的
 16         private $records = array();  
 17           
 18         public function __construct(Array $records) {
 19             $this->position = 0;
 20             $this->records = $records;
 21         }
 22           
 23         function rewind() {
 24             $this->position = 0;
 25         }
 26           
 27         function current() {
 28             return $this->records[$this->position];
 29         }
 30           
 31         function key() {
 32             return $this->position;
 33         }
 34           
 35         function next() {
 36             ++$this->position;
 37         }
 38           
 39         function valid() {
 40             return isset($this->records[$this->position]);
 41         }
 42     }
 43           
 44     class PostListPager {
 45           
 46         protected $record   = array();
 47         protected $total    = 0;
 48         protected $page     = 0;
 49         protected $size     = 0;
 50           
 51         public function __construct($category, $page, $size) {
 52           
 53             $this->page     = $page;
 54             $this->size     = $size;
 55           
 56             // query db
 57           
 58             $total          = 28;
 59             $this->total    = $total;
 60           
 61             $record     = array(
 62                             0 => array('id' => '1'),
 63                             1 => array('id' => '2'),
 64                             2 => array('id' => '3'),
 65                             3 => array('id' => '4'),
 66                         );
 67           
 68             //
 69             $this->record   = $record;
 70           
 71         }
 72           
 73         public function getIterator() {
 74             return  new RecordIterator($this->record);
 75         }
 76           
 77         public function getMaxPage() {
 78             $max    = intval($this->total / $this->size);
 79             return  $max;
 80         }
 81           
 82         public function getPrevPage() {
 83             return  max($this->page - 1, 1);
 84         }
 85           
 86         public function getNextPage() {
 87             return  min($this->page + 1, $this->getMaxPage());
 88         }
 89           
 90     }
 91           
 92     class Client {  
 93                 
 94         public static function test(){  
 95           
 96             $pager      = new PostListPager(1, 2, 4);
 97           
 98             foreach ($pager->getIterator() as $key => $val) {
 99                 output(sprintf('Key[%d],Val[%s]', $key, json_encode($val)));
100             }
101           
102             output(sprintf('MaxPage[%d]', $pager->getMaxPage()));
103             output(sprintf('Prev[%d]', $pager->getPrevPage()));
104             output(sprintf('Next[%d]', $pager->getNextPage()));
105           
106             $iterator       = $pager->getIterator();
107             while($iterator->valid()){
108                 print_r($iterator->current());
109                 $iterator->next();
110             }
111             $iterator->rewind();
112           
113         }  
114                 
115     }  
116             
117     Client::test();

4、观察者模式(Observer):

又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察 者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要 是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一 样的。

好处:广播式通信,范围大一呼百应,便于操作一个组团,“公有制”。

弊端:不能单独操作组团里的个体,不能实行按需分配。

应用场景:操作多个对象,并操作相同。

代码实现

  1     /**
  2      *
  3      * 观察者模式 Observer
  4      *
  5      */
  6           
  7     function output($string) {
  8         echo    $string . "n";
  9     }
 10           
 11           
 12     //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
 13     //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
 14     class Order{
 15         //订单号
 16         private $id = '';
 17           
 18         //用户ID
 19         private $userId = '';
 20           
 21         //用户名
 22         private $userName = '';
 23           
 24         //价格
 25         private $price = '';
 26           
 27         //下单时间
 28         private $orderTime = '';
 29           
 30         //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
 31         public function __set($name, $value){
 32             if (isset($this->$name)){
 33                 $this->$name = $value;
 34             }
 35         }
 36           
 37         //获取订单属性
 38         public function __get($name){
 39             if (isset($this->$name)){
 40                 return $this->$name;
 41             }
 42             return "";
 43         }
 44     }
 45           
 46     //假设的DB类,便于测试,实际会存入真实数据库
 47     class FakeDB{
 48         public function save($data){
 49             return true;
 50         }
 51     }
 52           
 53           
 54     class Client {
 55               
 56         public static function test() {
 57           
 58             //初始化一个订单数据
 59             $order = new Order();
 60             $order->id = 1001;
 61             $order->userId = 9527;
 62             $order->userName = "God";
 63             $order->price = 20.0;
 64             $order->orderTime = time();
 65           
 66             //向数据库保存订单
 67             $db = new FakeDB();
 68             $result = $db->save($order);
 69             if ($result){
 70           
 71                 //实际应用可能会写到日志文件中,这里直接输出
 72                 output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
 73           
 74                 //实际应用会调用邮件发送服务如sendmail,这里直接输出
 75                 output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
 76           
 77                 //实际应用会调用邮件发送服务如sendmail,这里直接输出
 78                 output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
 79           
 80             }
 81           
 82         }
 83           
 84     }
 85           
 86     Client::test();
 87           
 88           
 89     <?php
 90           
 91     /**
 92      *
 93      * 观察者模式 Observer
 94      *
 95      */
 96           
 97     function output($string) {
 98         echo    $string . "n";
 99     }
100           
101           
102     //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
103     //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
104     class Order{
105         //订单号
106         private $id = '';
107           
108         //用户ID
109         private $userId = '';
110           
111         //用户名
112         private $userName = '';
113           
114         //价格
115         private $price = '';
116           
117         //下单时间
118         private $orderTime = '';
119           
120         //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
121         public function __set($name, $value){
122             if (isset($this->$name)){
123                 $this->$name = $value;
124             }
125         }
126           
127         //获取订单属性
128         public function __get($name){
129             if (isset($this->$name)){
130                 return $this->$name;
131             }
132             return "";
133         }
134     }
135           
136     //被观察者, 负责维护观察者并在变化发生是通知观察者
137     class OrderSubject implements SplSubject {
138         private $observers;
139         private $order;
140           
141         public function __construct(Order $order) {
142             $this->observers = new SplObjectStorage();
143             $this->order = $order;
144         }
145           
146         //增加一个观察者
147         public function attach(SplObserver $observer) {
148             $this->observers->attach($observer);
149         }
150           
151         //移除一个观察者
152         public function detach(SplObserver $observer) {
153             $this->observers->detach($observer);
154         }
155           
156         //通知所有观察者
157         public function notify() {
158             foreach ($this->observers as $observer) {
159                 $observer->update($this);
160             }
161         }
162           
163         //返回主体对象的具体实现,供观察者调用
164         public function getOrder() {
165             return $this->order;
166         }
167     }
168           
169     //记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
170     class ActionLogObserver implements SplObserver{
171         public function update(SplSubject $subject) {
172              $order = $subject->getOrder();
173              //实际应用可能会写到日志文件中,这里直接输出
174              output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
175         }
176     }
177           
178     //给用户发送订单确认邮件 (UserMailObserver)
179     class UserMailObserver implements SplObserver{
180         public function update(SplSubject $subject) {
181              $order = $subject->getOrder();
182              //实际应用会调用邮件发送服务如sendmail,这里直接输出
183              output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
184         }
185     }
186           
187     //给管理人员发订单处理通知邮件 (AdminMailObserver)
188     class AdminMailObserver implements SplObserver{
189         public function update(SplSubject $subject) {
190              $order = $subject->getOrder();
191              //实际应用会调用邮件发送服务如sendmail,这里直接输出
192              output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
193         }
194     }
195           
196     //假设的DB类,便于测试,实际会存入真实数据库
197     class FakeDB{
198         public function save($data){
199             return true;
200         }
201     }
202           
203           
204     class Client {
205               
206         public static function test() {
207           
208             //初始化一个订单数据
209             $order = new Order();
210             $order->id = 1001;
211             $order->userId = 9527;
212             $order->userName = "God";
213             $order->price = 20.0;
214             $order->orderTime = time();
215           
216             //绑定观察者
217             $subject = new OrderSubject($order);
218             $actionLogObserver = new ActionLogObserver();
219             $userMailObserver = new UserMailObserver();
220             $adminMailObserver = new AdminMailObserver();
221             $subject->attach($actionLogObserver);
222             $subject->attach($userMailObserver);
223             $subject->attach($adminMailObserver);
224             //向数据库保存订单
225             $db = new FakeDB();
226             $result = $db->save($order);
227             if ($result){
228                 //通知观察者
229                 $subject->notify();
230             }
231           
232         }
233           
234     }
235           
236     Client::test();

5、中介者模式(Mediator):

用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引 用。

类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。

好处:简化了对象之间的关系,减少子类的生成。

弊端:中介对象可能变得非常复杂,系统难以维护。

应用场景:不需要显示地建立交互

代码实现

 1 /**
 2  *
 3  * 中介者模式 Mediator
 4  *
 5  */
 6       
 7       
 8 function output($string) {
 9     echo    $string . "n";
10 }
11       
12       
13       
14       
15 abstract class Mediator { // 中介者角色
16     abstract public function send($message,$colleague); 
17 } 
18       
19 abstract class Colleague { // 抽象对象
20     private $_mediator = null; 
21     public function __construct($mediator) { 
22         $this->_mediator = $mediator; 
23     } 
24     public function send($message) { 
25         $this->_mediator->send($message,$this); 
26     } 
27     abstract public function notify($message); 
28 } 
29       
30 class ConcreteMediator extends Mediator { // 具体中介者角色
31     private $_colleague1 = null; 
32     private $_colleague2 = null; 
33     public function send($message,$colleague) { 
34         if($colleague == $this->_colleague1) { 
35             $this->_colleague1->notify($message); 
36         } else { 
37             $this->_colleague2->notify($message); 
38         } 
39     }
40     public function set($colleague1,$colleague2) { 
41         $this->_colleague1 = $colleague1; 
42         $this->_colleague2 = $colleague2; 
43     } 
44 } 
45       
46 class Colleague1 extends Colleague { // 具体对象角色
47     public function notify($message) {
48         output(sprintf('Colleague-1: %s', $message));
49     } 
50 } 
51       
52 class Colleague2 extends Colleague { // 具体对象角色
53     public function notify($message) { 
54         output(sprintf('Colleague-2: %s', $message));
55     } 
56 } 
57       
58       
59       
60 class Client {  
61             
62     public static function test(){  
63       
64         // client
65         $objMediator = new ConcreteMediator(); 
66         $objC1 = new Colleague1($objMediator); 
67         $objC2 = new Colleague2($objMediator); 
68         $objMediator->set($objC1,$objC2); 
69         $objC1->send("to c2 from c1"); 
70         $objC2->send("to c1 from c2"); 
71       
72     }  
73             
74 }  
75         
76 Client::test();

6、状态模式(State) :

对象在不同状态下表现出不同的行为。就像女朋友一样,高兴了牵你的 手,不高兴了遛狗。在两种状态下变现出不同的行为。

好处:避免if语句实用,方便增加新状态,封装了状态转换规则。

弊端:增加系统类和对象的数量。

应用场景:用于对象的不同功能的转换。

代码实现

  1 /**
  2  * 状态模式 State
  3  *
  4  */
  5       
  6 function output($string) {
  7     echo    $string . "n";
  8 }
  9       
 10 abstract class ILift {  
 11       
 12     //电梯的四个状态  
 13     const OPENING_STATE = 1;  //门敞状态  
 14     const CLOSING_STATE = 2;  //门闭状态  
 15     const RUNNING_STATE = 3;  //运行状态  
 16     const STOPPING_STATE = 4; //停止状态;  
 17             
 18     //设置电梯的状态  
 19     public abstract function setState($state);  
 20         
 21     //首先电梯门开启动作  
 22     public abstract function open();  
 23         
 24     //电梯门有开启,那当然也就有关闭了  
 25     public abstract function close();  
 26         
 27     //电梯要能上能下,跑起来  
 28     public abstract function run();  
 29         
 30     //电梯还要能停下来
 31     public abstract function stop();  
 32       
 33 }  
 34         
 35 /** 
 36  * 电梯的实现类  
 37  */ 
 38 class Lift extends ILift {  
 39       
 40     private $state;  
 41         
 42     public function setState($state) {  
 43         $this->state = $state;  
 44     }  
 45       
 46     //电梯门关闭  
 47     public function close() {  
 48       
 49         //电梯在什么状态下才能关闭  
 50         switch ($this->state) {  
 51             case ILift::OPENING_STATE:  //如果是则可以关门,同时修改电梯状态  
 52                 $this->setState(ILift::CLOSING_STATE);  
 53             break;  
 54             case ILift::CLOSING_STATE:  //如果电梯就是关门状态,则什么都不做  
 55                 //do nothing;  
 56                 return ;  
 57             break;  
 58             case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做  
 59                 //do nothing;  
 60                 return ;  
 61             break;  
 62             case ILift::STOPPING_STATE:  //如果是停止状态,本也是关闭的,什么也不做  
 63                 //do nothing;  
 64                 return ;  
 65             break;  
 66         }  
 67       
 68         output('Lift colse');  
 69       
 70     }  
 71         
 72     //电梯门开启  
 73     public function open() {  
 74         //电梯在什么状态才能开启  
 75         switch($this->state){  
 76             case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做  
 77                 //do nothing;  
 78                 return ;  
 79             break;  
 80             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启  
 81                 $this->setState(ILift::OPENING_STATE);  
 82             break;  
 83             case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做  
 84             //do nothing;  
 85                 return ;  
 86             break;  
 87             case ILift::STOPPING_STATE: //停止状态,淡然要开门了  
 88                 $this->setState(ILift::OPENING_STATE);  
 89             break;  
 90         }  
 91         output('Lift open');  
 92     }  
 93     ///电梯开始跑起来  
 94     public function run() {  
 95         switch($this->state){  
 96             case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做  
 97                 //do nothing;  
 98                 return ;  
 99             break;  
100             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行  
101                 $this->setState(ILift::RUNNING_STATE);  
102             break;  
103             case ILift::RUNNING_STATE: //正在运行状态,则什么都不做  
104                 //do nothing;  
105                 return ;  
106             break;  
107             case ILift::STOPPING_STATE: //停止状态,可以运行  
108                 $this->setState(ILift::RUNNING_STATE);  
109         }  
110         output('Lift run');  
111     }  
112         
113     //电梯停止  
114     public function stop() {  
115         switch($this->state){  
116             case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做  
117                 //do nothing;  
118                 return ;  
119             break;  
120             case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了  
121                 $this->setState(ILift::CLOSING_STATE);  
122             break;  
123             case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了  
124                 $this->setState(ILift::CLOSING_STATE);  
125             break;  
126             case ILift::STOPPING_STATE: //停止状态,什么都不做  
127                 //do nothing;  
128                 return ;  
129             break;  
130         }  
131         output('Lift stop');  
132     }  
133             
134 }  
135       
136       
137       
138 class Client {
139           
140     public static function test() {
141       
142         $lift = new Lift();   
143                    
144         //电梯的初始条件应该是停止状态   
145         $lift->setState(ILift::STOPPING_STATE);   
146         //首先是电梯门开启,人进去   
147         $lift->open();   
148                    
149         //然后电梯门关闭   
150         $lift->close();   
151                    
152         //再然后,电梯跑起来,向上或者向下   
153         $lift->run();      
154       
155          //最后到达目的地,电梯挺下来   
156         $lift->stop();  
157       
158     }
159       
160 }
161       
162 Client::test();
163       
164       
165 <?php
166       
167 /**
168  *
169  * 状态模式 State
170  *
171  */
172       
173 function output($string) {
174     echo    $string . "n";
175 }
176       
177 /** 
178  *  
179  * 定义一个电梯的接口  
180  */ 
181 abstract class LiftState{  
182         
183     //定义一个环境角色,也就是封装状态的变换引起的功能变化  
184     protected  $_context;  
185         
186     public function setContext(Context $context){  
187         $this->_context = $context;  
188     }  
189         
190     //首先电梯门开启动作  
191     public abstract function open();  
192         
193     //电梯门有开启,那当然也就有关闭了  
194     public abstract function close();  
195         
196     //电梯要能上能下,跑起来  
197     public abstract function run();  
198         
199     //电梯还要能停下来,停不下来那就扯淡了  
200     public abstract function stop();  
201         
202 }  
203         
204         
205 /** 
206  * 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。 
207  */ 
208 class Context {  
209     //定义出所有的电梯状态  
210     static  $openningState = null;  
211     static  $closeingState = null;  
212     static  $runningState  = null;  
213     static  $stoppingState = null;  
214         
215     public function __construct() {  
216         self::$openningState = new OpenningState();  
217         self::$closeingState = new ClosingState();  
218         self::$runningState =  new RunningState();  
219         self::$stoppingState = new StoppingState();  
220         
221     }  
222         
223     //定一个当前电梯状态  
224     private  $_liftState;  
225         
226     public function getLiftState() {  
227         return $this->_liftState;  
228     }  
229         
230     public function setLiftState($liftState) {  
231         $this->_liftState = $liftState;  
232         //把当前的环境通知到各个实现类中  
233         $this->_liftState->setContext($this);  
234     }  
235         
236         
237     public function open(){  
238         $this->_liftState->open();  
239     }  
240         
241     public function close(){  
242         $this->_liftState->close();  
243     }  
244         
245     public function run(){  
246         $this->_liftState->run();  
247     }  
248         
249     public function stop(){  
250         $this->_liftState->stop();  
251     }  
252 }  
253         
254 /** 
255  * 在电梯门开启的状态下能做什么事情  
256  */ 
257 class OpenningState extends LiftState {  
258         
259     /** 
260      * 开启当然可以关闭了,我就想测试一下电梯门开关功能 
261      * 
262      */
263     public function close() {  
264         //状态修改  
265         $this->_context->setLiftState(Context::$closeingState);  
266         //动作委托为CloseState来执行  
267         $this->_context->getLiftState()->close();  
268     }  
269         
270     //打开电梯门  
271     public function open() {  
272         output('lift open...');
273     }  
274     //门开着电梯就想跑,这电梯,吓死你!  
275     public function run() {  
276         //do nothing;  
277     }  
278         
279     //开门还不停止?  
280     public function stop() {  
281         //do nothing;  
282     }  
283         
284 }  
285         
286 /** 
287  * 电梯门关闭以后,电梯可以做哪些事情  
288  */ 
289 class ClosingState extends LiftState {  
290         
291     //电梯门关闭,这是关闭状态要实现的动作  
292     public function close() {  
293         output('lift close...');
294         
295     }  
296     //电梯门关了再打开,逗你玩呢,那这个允许呀  
297     public function open() {  
298         $this->_context->setLiftState(Context::$openningState);  //置为门敞状态  
299         $this->_context->getLiftState()->open();  
300     }  
301         
302     //电梯门关了就跑,这是再正常不过了  
303     public function run() {  
304         $this->_context->setLiftState(Context::$runningState); //设置为运行状态;  
305         $this->_context->getLiftState()->run();  
306     }  
307         
308     //电梯门关着,我就不按楼层  
309             
310     public function stop() {  
311         $this->_context->setLiftState(Context::$stoppingState);  //设置为停止状态;  
312         $this->_context->getLiftState()->stop();  
313     }  
314         
315 }  
316         
317 /** 
318  * 电梯在运行状态下能做哪些动作  
319  */ 
320 class RunningState extends LiftState {  
321         
322     //电梯门关闭?这是肯定了  
323     public function close() {  
324         //do nothing  
325     }  
326         
327     //运行的时候开电梯门?你疯了!电梯不会给你开的  
328     public function open() {  
329         //do nothing  
330     }  
331         
332     //这是在运行状态下要实现的方法  
333     public function run() {  
334         output('lift run...');
335     }  
336         
337     //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了  
338     public function stop() {  
339         $this->_context->setLiftState(Context::$stoppingState); //环境设置为停止状态;  
340         $this->_context->getLiftState()->stop();  
341     }  
342         
343 }  
344         
345         
346         
347 /** 
348  * 在停止状态下能做什么事情  
349  */ 
350 class StoppingState extends LiftState {  
351         
352     //停止状态关门?电梯门本来就是关着的!  
353     public function close() {  
354         //do nothing;  
355     }  
356         
357     //停止状态,开门,那是要的!  
358     public function open() {  
359         $this->_context->setLiftState(Context::$openningState);  
360         $this->_context->getLiftState()->open();  
361     }  
362     //停止状态再跑起来,正常的很  
363     public function run() {  
364         $this->_context->setLiftState(Context::$runningState);  
365         $this->_context->getLiftState()->run();  
366     }  
367     //停止状态是怎么发生的呢?当然是停止方法执行了  
368     public function stop() {  
369         output('lift stop...');
370     }  
371         
372 }  
373         
374 /** 
375  * 模拟电梯的动作  
376  */ 
377 class Client {  
378         
379     public static function test() {  
380         $context = new Context();  
381         $context->setLiftState(new ClosingState());  
382         
383         $context->open();  
384         $context->close();  
385         $context->run();  
386         $context->stop();  
387     }  
388 }  
389       
390 Client::test();

7、职责链模式 (Chainof Responsibility):

多个对象有机会处理请求,为请求发送者和接收者解耦。就像银行里 的取款机,不管那一台都可以取到钱。

好处:简单化对象隐藏链结构,便于添加新职责节点。

弊端:请求可能没有接受者,或者被多个接收者调用,性能降低。

应用场景:处理多种请求。

代码实现

  1 /**
  2  * 职责链模式 Chain of Responsibility
  3  *
  4  */
  5       
  6 function output($string) {
  7     echo    $string . "n";
  8 }
  9       
 10 /** 
 11  * 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。 
 12   如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。 
 13   如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字, 
 14     如果请假天数超过10天,是任何人都不能批准的。
 15  */
 16       
 17         
 18 /** 
 19  * 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选) 
 20  * 
 21  */
 22 abstract class Handler 
 23 {  
 24       
 25     protected $_handler = null;  
 26     protected $_handlerName = null;  
 27             
 28     public function setSuccessor($handler)  
 29     {  
 30         $this->_handler = $handler;  
 31     }  
 32             
 33     protected  function _success($request)  
 34     {  
 35         output(sprintf("%s's request was passed", $request->getName())); 
 36         return true;  
 37     }  
 38     abstract function handleRequest($request);  
 39 }  
 40       
 41 /** 
 42  * 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。 
 43  * 
 44  */
 45 class ConcreteHandlerLeader extends Handler  
 46 {  
 47     function __construct($handlerName){  
 48         $this->_handlerName = $handlerName;  
 49     }  
 50     public function handleRequest($request)  
 51     {  
 52         if($request->getDay() < 0.5) {  
 53             output(sprintf('%s was told', $this->_handlerName));       // 已经跟leader招呼了
 54             return $this->_success($request);  
 55         }   
 56         if ($this->_handler instanceof Handler) {  
 57             return $this->_handler->handleRequest($request);  
 58         }  
 59     }  
 60 }  
 61 /** 
 62  * Manager 
 63  * 
 64  */
 65 class ConcreteHandlerManager extends Handler  
 66 {  
 67     function __construct($handlerName){  
 68         $this->_handlerName = $handlerName;  
 69     }  
 70             
 71     public function handleRequest($request)  
 72     {  
 73         if(0.5 <= $request->getDay() && $request->getDay()<=3) {  
 74             output(sprintf('%s signed', $this->_handlerName));       // 部门经理签字
 75             return $this->_success($request);  
 76         }   
 77         if ($this->_handler instanceof Handler) {  
 78             return $this->_handler->handleRequest($request);  
 79         }  
 80     }  
 81       
 82 }  
 83       
 84 class ConcreteHandlerGeneralManager extends Handler  
 85 {  
 86     function __construct($handlerName){  
 87         $this->_handlerName = $handlerName;  
 88     }  
 89             
 90     public function handleRequest($request)  
 91     {  
 92         if(3 < $request->getDay() && $request->getDay() < 10){  
 93             output(sprintf('%s signed', $this->_handlerName));       // 总经理签字
 94             return $this->_success($request);  
 95         }  
 96         if ($this->_handler instanceof Handler) {  
 97             return $this->_handler->handleRequest($request);  
 98         } else {
 99             output(sprintf('no one can approve request more than 10 days'));
100         }
101     }  
102       
103 }  
104       
105 /** 
106  * 请假申请 
107  * 
108  */
109 class Request  
110 {  
111     private $_name;  
112     private $_day;  
113     private $_reason;  
114         
115     function __construct($name= '', $day= 0, $reason = ''){  
116         $this->_name = $name;  
117         $this->_day = $day;  
118         $this->_reason = $reason;  
119     }  
120             
121     public function setName($name){  
122         $this->_name = $name;  
123     }  
124       
125     public function getName(){  
126         return  $this->_name;  
127     }  
128             
129     public function setDay($day){  
130         $this->_day = $day;  
131     }  
132       
133     public function getDay(){  
134         return  $this->_day ;  
135     }  
136             
137     public function setReason($reason ){  
138          $this->_reason = $reason;  
139     }  
140       
141     public function getReason( ){  
142         return  $this->_reason;  
143     }  
144 }  
145         
146         
147 class Client {  
148             
149     public static function test(){  
150                 
151         $leader = new ConcreteHandlerLeader('leader');  
152         $manager = new ConcreteHandlerManager('manager');  
153         $generalManager = new ConcreteHandlerGeneralManager('generalManager');  
154                 
155         //请求实例  
156         $request = new Request('ucai',4,'休息');  
157                 
158         $leader->setSuccessor($manager);  
159         $manager->setSuccessor($generalManager);  
160         $result =  $leader->handleRequest($request);  
161     }  
162             
163 }  
164         
165 Client::test();

8、策略模式(Strategy):

定义一系列算法,把每一个算法封装起来,并且使它们可相互替换。 就像篮球队里的球员,场上的和场下休息的。教练可以让场上的下来,也 可以让场下的上阵。

好处:定义可重用的一系列算法和行为,并且消除了if else语句。

弊端:调用端必须知道所有策略类。

应用场景:用于对象间的替换。

代码实现

 1 /**
 2  * 策略模式 Strategy
 3  *
 4  */
 5       
 6       
 7 function output($string) {
 8     echo    $string . "n";
 9 }
10       
11 //策略基类接口
12       
13 interface IStrategy {
14     public function OnTheWay();
15 }
16       
17 class WalkStrategy implements IStrategy {
18     public function OnTheWay() {
19         output( '在路上步行');
20     }
21 }
22       
23 class RideBickStrategy implements IStrategy {
24     public function OnTheWay() {
25         output( '在路上骑自行车');
26     }
27 }
28       
29 class CarStrategy implements IStrategy {
30     public function OnTheWay() {
31         output( '在路上开车');
32     }
33 }
34       
35 //选择策略类Context
36 class Context {
37     public function find($strategy) {
38         $strategy->OnTheWay();
39     }
40 }
41       
42 class Client {  
43             
44     public static function test(){  
45       
46         $travel = new Context();
47         $travel->find(new WalkStrategy());
48         $travel->find(new RideBickStrategy());
49         $travel->find(new CarStrategy());
50       
51     }  
52             
53 }  
54         
55 Client::test();

已知模式

1、备忘录模式(Memento):

保存对象在一时刻的状态。亲,还记得“老师来了记得叫我一下”的 同桌的他吗?

好处:给用户提供了一种可以恢复状态的机制。

弊端:消耗资源。

应用场景:用于需要保存的数据。

代码实现

  1 /**
  2  *
  3  * 备忘录模式 Memento
  4  *
  5  */
  6       
  7 function output($string) {
  8     echo    $string . "n";
  9 }
 10       
 11       
 12 class Originator { // 发起人(Originator)角色
 13     private $_state;
 14     public function __construct() {
 15         $this->_state = '';
 16     }
 17     public function createMemento() { // 创建备忘录
 18         return new Memento($this->_state);
 19     }
 20     public function restoreMemento(Memento $memento) { // 将发起人恢复到备忘录对象记录的状态上
 21         $this->_state = $memento->getState();
 22     }
 23     public function setState($state) { $this->_state = $state; } 
 24     public function getState() { return $this->_state; }
 25     public function showState() {
 26         output($this->_state);
 27     }
 28       
 29 }
 30       
 31 class Memento { // 备忘录(Memento)角色 
 32     private $_state;
 33     public function __construct($state) {
 34         $this->setState($state);
 35     }
 36     public function getState() { return $this->_state; } 
 37     public function setState($state) { $this->_state = $state;}
 38 }
 39       
 40 class Caretaker { // 负责人(Caretaker)角色 
 41     private $_memento;
 42     public function getMemento() { return $this->_memento; } 
 43     public function setMemento(Memento $memento) { $this->_memento = $memento; }
 44 }
 45       
 46 class Client {  
 47             
 48     public static function test(){  
 49       
 50         $org = new Originator();
 51         $org->setState('open');
 52         $org->showState();
 53       
 54         /* 创建备忘 */
 55         $memento = $org->createMemento();
 56       
 57         /* 通过Caretaker保存此备忘 */
 58         $caretaker = new Caretaker();
 59         $caretaker->setMemento($memento);
 60       
 61         /* 改变目标对象的状态 */
 62         $org->setState('close');
 63         $org->showState();
 64       
 65         /* 还原操作 */
 66         $org->restoreMemento($caretaker->getMemento());
 67         $org->showState();
 68       
 69     }  
 70             
 71 }  
 72         
 73 Client::test(); 
 74       
 75       
 76 return;
 77       
 78       
 79 try {
 80       
 81     $db->beginTransaction();
 82       
 83     $succ   = $db->exec($sql_1);
 84     if (!$succ) {
 85         throw new Exception('SQL 1 update failed');
 86     }
 87       
 88     $succ   = $db->exec($sql_2);
 89     if (!$succ) {
 90         throw new Exception('SQL 2 update failed');
 91     }
 92       
 93     $succ   = $db->exec($sql_3);
 94     if (!$succ) {
 95         throw new Exception('SQL 3 update failed');
 96     }
 97       
 98     $db->commit();
 99       
100 } catch (Exception $exp) {
101       
102     $db->rollBack();
103       
104 }

深度模式

1、解释器模式(Interpreter):

定义语言的文法,并建立一个解释器解释该语言中的句子。每个用过 字典的童鞋都懂滴。

好处:可扩展性比较好,灵活性大

弊端:可能难以维护复杂的文法

应用场景:用于成对或者一对多的需求中

2、访问者模式(Visitor):

封装某些用于作用于某种数据结构中各元素的操作,可以在不改变数 据结构的前提下定义作用于这些元素的新操作。如银行排号机。

好处:将相关的事物集中到一个访问者对象中。

弊端:增加新数据结构很困难。

应用场景:排队,排号。

三、总结

本篇介绍了行为型模式,行为模式涉及到算法和对象职责间的分配,行为类模式采用继承机制在类间分派行为,TemplateMethod和Interpreter是类行为

模式。行为对象模式使用对象复合而不是继承,一些行为对象模式描述了一组相互对等的对象如何相互协作以完成其中任何一个对象都单独无法完成的任务,如

Mediator在对象间引入一个mediator对象提供了松耦合所需的间接性;

Chain of Responsibility提供了更松的耦合,它通过一条候选对象链隐式的向一个对象发松请求,可以运行时刻决定哪些候选者参与到链中;

Observer定义并保持了对象间的依赖关系;其它的行为对象模式常将行为封装封装在一个对象中,并将请求指派给它,

Strategy模式将算法封装在对象中,这样可以方面的改变和指定一个对象所使用的算法;

Command模式将请求封装在对象中,这样它就可以作为参数来传递,已可以存储在历史列表中或以其它方式使用;

State模式封装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行为;

Visitor模式封装分布于多个类之间的行为;而Iterator模式则抽象了访问和遍历一个集合中对象的方式。