🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
 在上一篇我们讲了结构型模式,结构型模式是讨论类和对象的结构的。总共有7种。而今天我们来介绍一下行为型模式。 一、什么是行为型模式? 1、设计模式:   是一套被反复使用、多数人知晓的、经过分类编目的、代码设计的总结。就好像杯子,是被前人设计出来的,实现了储存水的功能,解决了人们的喝水问题。大多数人喝水都用杯子。但是你不必自己再重做另外方法再做一种容器,而实现的也是同样的功能,只要会用别人做出来的杯子喝水就能达到目的。但是杯子有很多中,实现喝水的方式也不同,比如茶水杯子,咖啡杯,啤酒杯子等等,要选择适合自己的杯子,就如茶水要用带过滤网的杯子,如果用不带过滤网的会喝一嘴茶叶。合适的才是最好的。 总结出来设计模式的特性如下: 1.普遍性:经过前辈们的使用,大多人都实用的,总结提炼不断的提升被普遍认为是实现某种事物的最有效的方法。 2.封装性:既然是方法那我们就不用太关心细节是怎么实现的,而主要是学习怎么用这些方法模式,从而达到自己的目的。 3.面向对象性:指挥对象做事。把复杂问题简单化。 4.最优性:要充分相信适合的就是最好的。 2、面向对象:   或许你对面向对象还有疑问?指挥对象做事,把复杂问题简单化。那么我们就来举个例子说明。比如我们去饭店吃饭,会叫服务员然后点菜,那我们就是指挥服务员做事,至于服务员怎么让厨师做,厨师怎么做,这些我们都不管。我们只管是否能吃到我们叫到的菜。这个就是把复杂事情简单化了。我们不用自己做菜,也不用知道菜怎么做,我们指挥服务员就行了,吃饭这件事就由复杂的做菜之类的变成吃这个简单的事了。 3、行为型模式: 就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。 二、行为型模式的种类   大体上分为三个大类:常见模式、已知模式、深度模式   常见模式包括: 模版方法模式命令模式 迭代器模式 观察者模式 中介者模式 状态式 职责链模式 策略模式 已知模式包括:备忘录模式 深度模式包括:解释器模式 访问者模式 下面来介绍常见模式 Ø 常见模式 1、模版方法模式(Template):   定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。   好处:扩展性好,封装不变的代码,扩展可变的代码   弊端:灵活性差,不能改变骨架部分。   应用场景:一类或一组具有共性的事物中 代码实现 <?php /** * 优才网公开课示例代码 * * 模板方法模式 Template * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } class Request { public$token = ''; publicfunction __construct() { $this->token ='0c6b7289f5334ed2b697dd461eaf9812'; } } class Response { publicfunction render($content) { output(sprintf('response-render: %s',$content)); } publicfunction redirect($uri) { output(sprintf('response-redirect: %s', $uri)); } publicfunction json($data) { output(sprintf('response-data: %s', json_encode($data))); } } //父类,抽象类 abstract class Controller{ //封装了输入输出 protected$request; protected$response; //返回数据 protected$data = 'data'; publicfunction __construct($request, $response){ $this->request = $request; $this->response = $response; } //执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法) publicfinal function execute(){ $this->before(); if($this->valid()){ $this->handleRequest(); } $this->after(); } //定义hook methodbefore,做一些具体请求的前置处理 //非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做 protectedfunction before(){ } //定义hook methodvalid,做请求的数据验证 //非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过 protectedfunction valid(){ returntrue; } //定义hook methodhandleRequest,处理请求 //定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现) abstractfunction handleRequest(); //定义hook methodafter,做一些请求的后置处理 //非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据 protectedfunction after(){ $this->response->render($this->data); } } //子类1,实现父类开放的具体算法 class User extends Controller{ //覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器 //因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据 functionbefore(){ if(empty($_SESSION['auth'])){ //没登录就直接跳转了,不再执行后续的操作 $this->response->redirect("user/login.php"); } } //覆盖valid方法,这里我们验证用户提交数据中有没有带验证token functionvalid(){ if(isset($this->request->token)){ return true; } returnfalse; } //覆盖handleRequest方法,必选,以为父类中声明了abstract了 functionhandleRequest(){ //做具体处理,一般根据参数执行不同的业务逻辑 } //这个类我们选择不覆盖after方法,使用默认处理方式 } //子类2,实现父类开放的具体算法 class Post extends Controller{ //这个类我们选择不覆盖before方法,使用默认处理方式 //这个类我们选择不覆盖valid方法,使用默认处理方式 //覆盖handleRequest方法,必选,以为父类中声明了abstract了 functionhandleRequest(){ //做具体处理,一般根据参数执行不同的业务逻辑 $this->data = array('title' => 'ucai'); } //覆盖after方法,使用json格式输出数据 functionafter(){ $this->response->json($this->data); } } class Client { publicstatic 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) :   行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到这个命令都会做出标准的敬礼动作。   好处:便于添加和修改行为,便于聚合多个命令   弊端:造成过多具体的命令类   应用场景:对要操作的对象,进行的相同操作 代码实现 <?php /** * 优才网公开课示例代码 * * 命令模式 Command * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } class Document { private$name = ''; public function __construct($name) { $this->name = $name; } publicfunction showText() { output(sprintf("showText: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics { private$name = ''; publicfunction __construct($name) { $this->name = $name; } publicfunction drawCircle() { output(sprintf("drawCircle: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class Client { publicstatic function test() { $document = newDocument('A'); $graphics = newGraphics('B'); $document->showText(); $graphics->drawCircle(); $document->undo(); } } Client::test(); <?php /** * 优才网公开课示例代码 * * 命令模式 Command * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } interface Command { publicfunction execute(); publicfunction undo(); } class Document implements Command { private$name = ''; publicfunction __construct($name) { $this->name = $name; } publicfunction execute() { output(sprintf("showText: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics implements Command { private$name = ''; publicfunction __construct($name) { $this->name = $name; } publicfunction execute() { output(sprintf("drawCircle: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class Client { publicstatic 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 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } interface Command { publicfunction execute(); publicfunction undo(); } class Document { private$name = ''; publicfunction __construct($name) { $this->name = $name; } publicfunction showText() { output(sprintf("showText: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-showText: %s", $this->name)); } } class Graphics { private$name = ''; publicfunction __construct($name) { $this->name = $name; } publicfunction drawCircle() { output(sprintf("drawCircle: %s", $this->name)); } publicfunction undo() { output(sprintf("undo-drawCircle: %s", $this->name)); } } class DocumentCommand implements Command { private$obj = ''; publicfunction __construct(Document $document) { $this->obj = $document; } publicfunction execute() { $this->obj->showText(); } publicfunction undo() { $this->obj->undo(); } } class GraphicsCommand implements Command { private$obj = ''; publicfunction __construct(Graphics $graphics) { $this->obj = $graphics; } publicfunction execute() { $this->obj->drawCircle(); } publicfunction undo() { $this->obj->undo(); } } class Client { publicstatic 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(newGraphics('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):   访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。   好处:以不同方式遍历一个集合   弊端:每次遍历都是整个集合,不能单独取出元素   应用场景:需要操作集合里的全部元素。 代码实现: <?php /** * 优才网公开课示例代码 * * 迭代器模式 Iterator * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } class RecordIterator implements Iterator{ private$position = 0; //注意:被迭代对象属性是私有的 private$records = array(); publicfunction __construct(Array $records) { $this->position = 0; $this->records = $records; } functionrewind() { $this->position = 0; } functioncurrent() { return$this->records[$this->position]; } functionkey() { return$this->position; } functionnext() { ++$this->position; } functionvalid() { returnisset($this->records[$this->position]); } } class PostListPager { protected$record = array(); protected$total = 0; protected$page = 0; protected$size = 0; publicfunction __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; } publicfunction getIterator() { return newRecordIterator($this->record); } publicfunction getMaxPage() { $max = intval($this->total /$this->size); return $max; } publicfunction getPrevPage() { return max($this->page - 1,1); } publicfunction getNextPage() { return min($this->page + 1,$this->getMaxPage()); } } class Client { publicstatic 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):   又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一样的。   好处:广播式通信,范围大,一呼百应,便于操作一个组团,“公有制”   弊端:不能单独操作组团里的个体,不能实行按需分配   应用场景:操作多个对象,并操作相同。 代码实现: <?php /** * 优才网公开课示例代码 * * 观察者模式 Observer * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后 //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立 class Order{ //订单号 private$id = ''; //用户ID private$userId = ''; //用户名 private$userName = ''; //价格 private$price = ''; //下单时间 private$orderTime = ''; //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理 publicfunction __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{ publicfunction save($data){ returntrue; } } class Client { publicstatic 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} wasconfirmed!" ); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear Manager: User{$order->userName}(ID:{$order->userId}) submitted a new order {$order->id},please handle it ASAP!" ); } } } Client::test(); <?php /** * 优才网公开课示例代码 * * 观察者模式 Observer * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后 //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立 class Order{ //订单号 private$id = ''; //用户ID private$userId = ''; //用户名 private$userName = ''; //价格 private$price = ''; //下单时间 private$orderTime = ''; //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理 publicfunction __set($name, $value){ if(isset($this->$name)){ $this->$name = $value; } } //获取订单属性 publicfunction __get($name){ if(isset($this->$name)){ return $this->$name; } return""; } } //被观察者, 负责维护观察者并在变化发生是通知观察者 class OrderSubject implements SplSubject { private$observers; private$order; publicfunction __construct(Order $order) { $this->observers = new SplObjectStorage(); $this->order = $order; } //增加一个观察者 publicfunction attach(SplObserver $observer) { $this->observers->attach($observer); } //移除一个观察者 publicfunction detach(SplObserver $observer) { $this->observers->detach($observer); } //通知所有观察者 publicfunction notify() { foreach ($this->observers as $observer) { $observer->update($this); } } //返回主体对象的具体实现,供观察者调用 publicfunction getOrder() { return$this->order; } } //记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略 class ActionLogObserver implements SplObserver{ publicfunction update(SplSubject $subject) { $order = $subject->getOrder(); //实际应用可能会写到日志文件中,这里直接输出 output( "[OrderId:{$order->id}] [UseId:{$order->userId}][Price:{$order->price}]" ); } } //给用户发送订单确认邮件 (UserMailObserver) class UserMailObserver implements SplObserver{ publicfunction update(SplSubject $subject) { $order = $subject->getOrder(); //实际应用会调用邮件发送服务如sendmail,这里直接输出 output( "Dear {$order->userName}: Your order {$order->id} wasconfirmed!" ); } } //给管理人员发订单处理通知邮件 (AdminMailObserver) class AdminMailObserver implements SplObserver{ publicfunction 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{ publicfunction save($data){ returntrue; } } class Client { publicstatic 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 = newUserMailObserver(); $adminMailObserver = new AdminMailObserver(); $subject->attach($actionLogObserver); $subject->attach($userMailObserver); $subject->attach($adminMailObserver); //向数据库保存订单 $db =new FakeDB(); $result = $db->save($order); if($result){ //通知观察者 $subject->notify(); } } } Client::test(); 5、中介者模式(Mediator):   用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引用。类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。   好处:简化了对象之间的关系,减少子类的生成   弊端:中介对象可能变得非常复杂,系统难以维护   应用场景:不需要显示地建立交互 代码实现: <?php /** * 优才网公开课示例代码 * * 中介者模式 Mediator * * @author 优才网全栈工程师教研组 * @seehttp://www.ucai.cn */ function output($string) { echo $string . "\n"; } abstract class Mediator { // 中介者角色 abstractpublic function send($message,$colleague); } abstract class Colleague { // 抽象对象 private$_mediator = null; publicfunction __construct($mediator) { $this->_mediator = $mediator; } publicfunction send($message) { $this->_mediator->send($message,$this); } abstractpublic function notify($message); } class ConcreteMediator extends Mediator { // 具体中介者角色 private$_colleague1 = null; private$_colleague2 = null; publicfunction send($message,$colleague) { if($colleague == $this->_colleague1) { $this->_colleague1->notify($message); } else{ $this->_colleague2->notify($message); } } publicfunction set($colleague1,$colleague2) { $this->_colleague1 = $colleague1; $this->_colleague2 = $colleague2; } } class Colleague1 extends Colleague { // 具体对象角色 publicfunction notify($message) { output(sprintf('Colleague-1: %s', $message)); } } class Colleague2 extends Colleague { // 具体对象角色 publicfunction notify($message) { output(sprintf('Colleague-2: %s', $message)); } } class Client { publicstatic function test(){ //client $objMediator = new ConcreteMediator(); $objC1= new Colleague1($objMediator); $objC2= new Colleague2($objMediator); $objMediator->set($objC1,$objC2); $objC1->send("to c2 from c1"); $objC2->send("to c1 from c2"); } } Client::test(); 说明:剩下的几种模式将在下次发布,敬请关注。