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


一、什么是行为型模式?

行为型模式:

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


二、行为型模式的种类

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

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

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

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

下面来介绍常见模式

常见模式

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

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

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

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

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

代码实现


<?php/***优才网公开课示例代码**模板方法模式Template**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}classRequest{public$token='';publicfunction__construct(){$this->token='0c6b7289f5334ed2b697dd461eaf9812';}}classResponse{publicfunctionrender($content){output(sprintf('response-render:%s',$content));}publicfunctionredirect($uri){output(sprintf('response-redirect:%s',$uri));}publicfunctionjson($data){output(sprintf('response-data:%s',json_encode($data)));}}//父类,抽象类abstractclassController{//封装了输入输出protected$request;protected$response;//返回数据protected$data='data';publicfunction__construct($request,$response){$this->request=$request;$this->response=$response;}//执行请求函数,定义总体算法(templatemethod),final防止被复写(不允许子类改变总体算法)publicfinalfunctionexecute(){$this->before();if($this->valid()){$this->handleRequest();}$this->after();}//定义hookmethodbefore,做一些具体请求的前置处理//非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做protectedfunctionbefore(){}//定义hookmethodvalid,做请求的数据验证//非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过protectedfunctionvalid(){returntrue;}//定义hookmethodhandleRequest,处理请求//定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)abstractfunctionhandleRequest();//定义hookmethodafter,做一些请求的后置处理//非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据protectedfunctionafter(){$this->response->render($this->data);}}//子类1,实现父类开放的具体算法classUserextendsController{//覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器//因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据functionbefore(){if(empty($_SESSION['auth'])){//没登录就直接跳转了,不再执行后续的操作$this->response->redirect("user/login.php");}}//覆盖valid方法,这里我们验证用户提交数据中有没有带验证tokenfunctionvalid(){if(isset($this->request->token)){returntrue;}returnfalse;}//覆盖handleRequest方法,必选,以为父类中声明了abstract了functionhandleRequest(){//做具体处理,一般根据参数执行不同的业务逻辑}//这个类我们选择不覆盖after方法,使用默认处理方式}//子类2,实现父类开放的具体算法classPostextendsController{//这个类我们选择不覆盖before方法,使用默认处理方式//这个类我们选择不覆盖valid方法,使用默认处理方式//覆盖handleRequest方法,必选,以为父类中声明了abstract了functionhandleRequest(){//做具体处理,一般根据参数执行不同的业务逻辑$this->data=array('title'=>'ucai');}//覆盖after方法,使用json格式输出数据functionafter(){$this->response->json($this->data);}}classClient{publicstaticfunctiontest(){$request=newRequest();$response=newResponse();//最终调用$user=newUser($request,$response);$user->execute();//最终调用$post=newPost($request,$response);$post->execute();}}Client::test();



2、命令模式(Command) :

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

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

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

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

代码实现


<?php/***优才网公开课示例代码**命令模式Command**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}classDocument{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctionshowText(){output(sprintf("showText:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-showText:%s",$this->name));}}classGraphics{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctiondrawCircle(){output(sprintf("drawCircle:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-drawCircle:%s",$this->name));}}classClient{publicstaticfunctiontest(){$document=newDocument('A');$graphics=newGraphics('B');$document->showText();$graphics->drawCircle();$document->undo();}}Client::test();<?php/***优才网公开课示例代码**命令模式Command**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}interfaceCommand{publicfunctionexecute();publicfunctionundo();}classDocumentimplementsCommand{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctionexecute(){output(sprintf("showText:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-showText:%s",$this->name));}}classGraphicsimplementsCommand{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctionexecute(){output(sprintf("drawCircle:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-drawCircle:%s",$this->name));}}classClient{publicstaticfunctiontest(){$array=array();array_push($array,newDocument('A'));array_push($array,newDocument('B'));array_push($array,newGraphics('C'));array_push($array,newGraphics('D'));foreach($arrayas$command){$command->execute();}$top=array_pop($array);$top->undo();}}Client::test();<?php/***优才网公开课示例代码**命令模式Command**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}interfaceCommand{publicfunctionexecute();publicfunctionundo();}classDocument{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctionshowText(){output(sprintf("showText:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-showText:%s",$this->name));}}classGraphics{private$name='';publicfunction__construct($name){$this->name=$name;}publicfunctiondrawCircle(){output(sprintf("drawCircle:%s",$this->name));}publicfunctionundo(){output(sprintf("undo-drawCircle:%s",$this->name));}}classDocumentCommandimplementsCommand{private$obj='';publicfunction__construct(Document$document){$this->obj=$document;}publicfunctionexecute(){$this->obj->showText();}publicfunctionundo(){$this->obj->undo();}}classGraphicsCommandimplementsCommand{private$obj='';publicfunction__construct(Graphics$graphics){$this->obj=$graphics;}publicfunctionexecute(){$this->obj->drawCircle();}publicfunctionundo(){$this->obj->undo();}}classClient{publicstaticfunctiontest(){$array=array();array_push($array,newDocumentCommand(newDocument('A')));array_push($array,newDocumentCommand(newDocument('B')));array_push($array,newGraphicsCommand(newGraphics('C')));array_push($array,newGraphicsCommand(newGraphics('D')));foreach($arrayas$command){$command->execute();}$top=array_pop($array);$top->undo();}}Client::test();


3、迭代器模式(Iterator):

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

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

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

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

代码实现:


<?php/***优才网公开课示例代码**迭代器模式Iterator**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}classRecordIteratorimplementsIterator{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]);}}classPostListPager{protected$record=array();protected$total=0;protected$page=0;protected$size=0;publicfunction__construct($category,$page,$size){$this->page=$page;$this->size=$size;//querydb$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;}publicfunctiongetIterator(){returnnewRecordIterator($this->record);}publicfunctiongetMaxPage(){$max=intval($this->total/$this->size);return$max;}publicfunctiongetPrevPage(){returnmax($this->page-1,1);}publicfunctiongetNextPage(){returnmin($this->page+1,$this->getMaxPage());}}classClient{publicstaticfunctiontest(){$pager=newPostListPager(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*/functionoutput($string){echo$string."\n";}//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立classOrder{//订单号private$id='';//用户IDprivate$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"";}}//假设的DB类,便于测试,实际会存入真实数据库classFakeDB{publicfunctionsave($data){returntrue;}}classClient{publicstaticfunctiontest(){//初始化一个订单数据$order=newOrder();$order->id=1001;$order->userId=9527;$order->userName="God";$order->price=20.0;$order->orderTime=time();//向数据库保存订单$db=newFakeDB();$result=$db->save($order);if($result){//实际应用可能会写到日志文件中,这里直接输出output("[OrderId:{$order->id}][UseId:{$order->userId}][Price:{$order->price}]");//实际应用会调用邮件发送服务如sendmail,这里直接输出output("Dear{$order->userName}:Yourorder{$order->id}wasconfirmed!");//实际应用会调用邮件发送服务如sendmail,这里直接输出output("DearManager:User{$order->userName}(ID:{$order->userId})submittedaneworder{$order->id},pleasehandleitASAP!");}}}Client::test();<?php/***优才网公开课示例代码**观察者模式Observer**@author优才网全栈工程师教研组*@seehttp://www.ucai.cn*/functionoutput($string){echo$string."\n";}//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立classOrder{//订单号private$id='';//用户IDprivate$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"";}}//被观察者,负责维护观察者并在变化发生是通知观察者classOrderSubjectimplementsSplSubject{private$observers;private$order;publicfunction__construct(Order$order){$this->observers=newSplObjectStorage();$this->order=$order;}//增加一个观察者publicfunctionattach(SplObserver$observer){$this->observers->attach($observer);}//移除一个观察者publicfunctiondetach(SplObserver$observer){$this->observers->detach($observer);}//通知所有观察者publicfunctionnotify(){foreach($this->observersas$observer){$observer->update($this);}}//返回主体对象的具体实现,供观察者调用publicfunctiongetOrder(){return$this->order;}}//记录业务数据日志(ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略classActionLogObserverimplementsSplObserver{publicfunctionupdate(SplSubject$subject){$order=$subject->getOrder();//实际应用可能会写到日志文件中,这里直接输出output("[OrderId:{$order->id}][UseId:{$order->userId}][Price:{$order->price}]");}}//给用户发送订单确认邮件(UserMailObserver)classUserMailObserverimplementsSplObserver{publicfunctionupdate(SplSubject$subject){$order=$subject->getOrder();//实际应用会调用邮件发送服务如sendmail,这里直接输出output("Dear{$order->userName}:Yourorder{$order->id}wasconfirmed!");}}//给管理人员发订单处理通知邮件(AdminMailObserver)classAdminMailObserverimplementsSplObserver{publicfunctionupdate(SplSubject$subject){$order=$subject->getOrder();//实际应用会调用邮件发送服务如sendmail,这里直接输出output("DearManager:User{$order->userName}(ID:{$order->userId})submittedaneworder{$order->id},pleasehandleitASAP!");}}//假设的DB类,便于测试,实际会存入真实数据库classFakeDB{publicfunctionsave($data){returntrue;}}classClient{publicstaticfunctiontest(){//初始化一个订单数据$order=newOrder();$order->id=1001;$order->userId=9527;$order->userName="God";$order->price=20.0;$order->orderTime=time();//绑定观察者$subject=newOrderSubject($order);$actionLogObserver=newActionLogObserver();$userMailObserver=newUserMailObserver();$adminMailObserver=newAdminMailObserver();$subject->attach($actionLogObserver);$subject->attach($userMailObserver);$subject->attach($adminMailObserver);//向数据库保存订单$db=newFakeDB();$result=$db->save($order);if($result){//通知观察者$subject->notify();}}}Client::test();


欲知后事如何,且听下回分解~·~