一、什么是行为型模式?
行为型模式就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
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模式则抽象了访问和遍历一个集合中对象的方式。
原文地址:https://www.cnblogs.com/hb01846/p/10946488.html