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

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

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

《接上文》

5、中介者模式(Mediator) :

      用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引用。类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。

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

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

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

代码实现:

<?php

/**
 * 优才网公开课示例代码
 *
 * 中介者模式 Mediator
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */


function output($string) {
    echo    $string . "\n";
}




abstract class Mediator { // 中介者角色
    abstract public function send($message,$colleague); 
} 

abstract class Colleague { // 抽象对象
    private $_mediator = null; 
    public function __construct($mediator) { 
        $this->_mediator = $mediator; 
    } 
    public function send($message) { 
        $this->_mediator->send($message,$this); 
    } 
    abstract public function notify($message); 
} 

class ConcreteMediator extends Mediator { // 具体中介者角色
    private $_colleague1 = null; 
    private $_colleague2 = null; 
    public function send($message,$colleague) { 
        if($colleague == $this->_colleague1) { 
            $this->_colleague1->notify($message); 
        } else { 
            $this->_colleague2->notify($message); 
        } 
    }
    public function set($colleague1,$colleague2) { 
        $this->_colleague1 = $colleague1; 
        $this->_colleague2 = $colleague2; 
    } 
} 

class Colleague1 extends Colleague { // 具体对象角色
    public function notify($message) {
        output(sprintf('Colleague-1: %s', $message));
    } 
} 

class Colleague2 extends Colleague { // 具体对象角色
    public function notify($message) { 
        output(sprintf('Colleague-2: %s', $message));
    } 
} 



class Client {  
      
    public static 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(); 

  

6、状态模式(State) :

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

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

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

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

代码实现:

<?php

/**
 * 优才网公开课示例代码
 *
 * 状态模式 State
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */

function output($string) {
    echo    $string . "\n";
}

abstract class ILift {  

    //电梯的四个状态  
    const OPENING_STATE = 1;  //门敞状态  
    const CLOSING_STATE = 2;  //门闭状态  
    const RUNNING_STATE = 3;  //运行状态  
    const STOPPING_STATE = 4; //停止状态;  
      
    //设置电梯的状态  
    public abstract function setState($state);  
  
    //首先电梯门开启动作  
    public abstract function open();  
  
    //电梯门有开启,那当然也就有关闭了  
    public abstract function close();  
  
    //电梯要能上能下,跑起来  
    public abstract function run();  
  
    //电梯还要能停下来
    public abstract function stop();  

}  
  
/** 
 * 电梯的实现类  
 */   
class Lift extends ILift {  

    private $state;  
  
    public function setState($state) {  
        $this->state = $state;  
    }  

    //电梯门关闭  
    public function close() {  

        //电梯在什么状态下才能关闭  
        switch ($this->state) {  
            case ILift::OPENING_STATE:  //如果是则可以关门,同时修改电梯状态  
                $this->setState(ILift::CLOSING_STATE);  
            break;  
            case ILift::CLOSING_STATE:  //如果电梯就是关门状态,则什么都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::STOPPING_STATE:  //如果是停止状态,本也是关闭的,什么也不做  
                //do nothing;  
                return ;  
            break;  
        }  

        output('Lift colse');  

    }  
  
    //电梯门开启  
    public function open() {  
        //电梯在什么状态才能开启  
        switch($this->state){  
            case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启  
                $this->setState(ILift::OPENING_STATE);  
            break;  
            case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做  
            //do nothing;  
                return ;  
            break;  
            case ILift::STOPPING_STATE: //停止状态,淡然要开门了  
                $this->setState(ILift::OPENING_STATE);  
            break;  
        }  
        output('Lift open');  
    }  
    ///电梯开始跑起来  
    public function run() {  
        switch($this->state){  
            case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行  
                $this->setState(ILift::RUNNING_STATE);  
            break;  
            case ILift::RUNNING_STATE: //正在运行状态,则什么都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::STOPPING_STATE: //停止状态,可以运行  
                $this->setState(ILift::RUNNING_STATE);  
        }  
        output('Lift run');  
    }  
  
    //电梯停止  
    public function stop() {  
        switch($this->state){  
            case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做  
                //do nothing;  
                return ;  
            break;  
            case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了  
                $this->setState(ILift::CLOSING_STATE);  
            break;  
            case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了  
                $this->setState(ILift::CLOSING_STATE);  
            break;  
            case ILift::STOPPING_STATE: //停止状态,什么都不做  
                //do nothing;  
                return ;  
            break;  
        }  
        output('Lift stop');  
    }  
      
}  



class Client {
    
    public static function test() {

        $lift = new Lift();   
             
        //电梯的初始条件应该是停止状态   
        $lift->setState(ILift::STOPPING_STATE);   
        //首先是电梯门开启,人进去   
        $lift->open();   
             
        //然后电梯门关闭   
        $lift->close();   
             
        //再然后,电梯跑起来,向上或者向下   
        $lift->run();      

         //最后到达目的地,电梯挺下来   
        $lift->stop();  

    }

}

Client::test();

  

<?php

/**
 * 优才网公开课示例代码
 *
 * 状态模式 State
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */

function output($string) {
    echo    $string . "\n";
}

/** 
 *  
 * 定义一个电梯的接口  
 */   
abstract class LiftState{  
  
    //定义一个环境角色,也就是封装状态的变换引起的功能变化  
    protected  $_context;  
  
    public function setContext(Context $context){  
        $this->_context = $context;  
    }  
  
    //首先电梯门开启动作  
    public abstract function open();  
  
    //电梯门有开启,那当然也就有关闭了  
    public abstract function close();  
  
    //电梯要能上能下,跑起来  
    public abstract function run();  
  
    //电梯还要能停下来,停不下来那就扯淡了  
    public abstract function stop();  
  
}  
  
  
/** 
 * 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。 
 */   
class Context {  
    //定义出所有的电梯状态  
    static  $openningState = null;  
    static  $closeingState = null;  
    static  $runningState  = null;  
    static  $stoppingState = null;  
  
    public function __construct() {  
        self::$openningState = new OpenningState();  
        self::$closeingState = new ClosingState();  
        self::$runningState =  new RunningState();  
        self::$stoppingState = new StoppingState();  
  
    }  
  
    //定一个当前电梯状态  
    private  $_liftState;  
  
    public function getLiftState() {  
        return $this->_liftState;  
    }  
  
    public function setLiftState($liftState) {  
        $this->_liftState = $liftState;  
        //把当前的环境通知到各个实现类中  
        $this->_liftState->setContext($this);  
    }  
  
  
    public function open(){  
        $this->_liftState->open();  
    }  
  
    public function close(){  
        $this->_liftState->close();  
    }  
  
    public function run(){  
        $this->_liftState->run();  
    }  
  
    public function stop(){  
        $this->_liftState->stop();  
    }  
}  
  
/** 
 * 在电梯门开启的状态下能做什么事情  
 */   
class OpenningState extends LiftState {  
  
    /** 
     * 开启当然可以关闭了,我就想测试一下电梯门开关功能 
     * 
     */  
    public function close() {  
        //状态修改  
        $this->_context->setLiftState(Context::$closeingState);  
        //动作委托为CloseState来执行  
        $this->_context->getLiftState()->close();  
    }  
  
    //打开电梯门  
    public function open() {  
        output('lift open...');
    }  
    //门开着电梯就想跑,这电梯,吓死你!  
    public function run() {  
        //do nothing;  
    }  
  
    //开门还不停止?  
    public function stop() {  
        //do nothing;  
    }  
  
}  
  
/** 
 * 电梯门关闭以后,电梯可以做哪些事情  
 */   
class ClosingState extends LiftState {  
  
    //电梯门关闭,这是关闭状态要实现的动作  
    public function close() {  
        output('lift close...');
  
    }  
    //电梯门关了再打开,逗你玩呢,那这个允许呀  
    public function open() {  
        $this->_context->setLiftState(Context::$openningState);  //置为门敞状态  
        $this->_context->getLiftState()->open();  
    }  
  
    //电梯门关了就跑,这是再正常不过了  
    public function run() {  
        $this->_context->setLiftState(Context::$runningState); //设置为运行状态;  
        $this->_context->getLiftState()->run();  
    }  
  
    //电梯门关着,我就不按楼层  
      
    public function stop() {  
        $this->_context->setLiftState(Context::$stoppingState);  //设置为停止状态;  
        $this->_context->getLiftState()->stop();  
    }  
  
}  
  
/** 
 * 电梯在运行状态下能做哪些动作  
 */   
class RunningState extends LiftState {  
  
    //电梯门关闭?这是肯定了  
    public function close() {  
        //do nothing  
    }  
  
    //运行的时候开电梯门?你疯了!电梯不会给你开的  
    public function open() {  
        //do nothing  
    }  
  
    //这是在运行状态下要实现的方法  
    public function run() {  
        output('lift run...');
    }  
  
    //这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了  
    public function stop() {  
        $this->_context->setLiftState(Context::$stoppingState); //环境设置为停止状态;  
        $this->_context->getLiftState()->stop();  
    }  
  
}  
  
  
  
/** 
 * 在停止状态下能做什么事情  
 */   
class StoppingState extends LiftState {  
  
    //停止状态关门?电梯门本来就是关着的!  
    public function close() {  
        //do nothing;  
    }  
  
    //停止状态,开门,那是要的!  
    public function open() {  
        $this->_context->setLiftState(Context::$openningState);  
        $this->_context->getLiftState()->open();  
    }  
    //停止状态再跑起来,正常的很  
    public function run() {  
        $this->_context->setLiftState(Context::$runningState);  
        $this->_context->getLiftState()->run();  
    }  
    //停止状态是怎么发生的呢?当然是停止方法执行了  
    public function stop() {  
        output('lift stop...');
    }  
  
}  
  
/** 
 * 模拟电梯的动作  
 */   
class Client {  
  
    public static function test() {  
        $context = new Context();  
        $context->setLiftState(new ClosingState());  
  
        $context->open();  
        $context->close();  
        $context->run();  
        $context->stop();  
    }  
}  

Client::test();  

 

7、职责链模式 (Chain of Responsibility):

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

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

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

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

代码实现:

 

<?php

/**
 * 优才网公开课示例代码
 *
 * 职责链模式 Chain of Responsibility
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */

function output($string) {
    echo    $string . "\n";
}

/** 
 * 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。 
  如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。 
  如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字, 
    如果请假天数超过10天,是任何人都不能批准的。
 */ 

  
/** 
 * 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选) 
 * 
 */  
abstract class Handler 
{  

    protected $_handler = null;  
    protected $_handlerName = null;  
      
    public function setSuccessor($handler)  
    {  
        $this->_handler = $handler;  
    }  
      
    protected  function _success($request)  
    {  
        output(sprintf("%s's request was passed", $request->getName())); 
        return true;  
    }  
    abstract function handleRequest($request);  
}  

/** 
 * 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。 
 * 
 */  
class ConcreteHandlerLeader extends Handler  
{  
    function __construct($handlerName){  
        $this->_handlerName = $handlerName;  
    }  
    public function handleRequest($request)  
    {  
        if($request->getDay() < 0.5) {  
            output(sprintf('%s was told', $this->_handlerName));       // 已经跟leader招呼了
            return $this->_success($request);  
        }   
        if ($this->_handler instanceof Handler) {  
            return $this->_handler->handleRequest($request);  
        }  
    }  
}  
/** 
 * Manager 
 * 
 */  
class ConcreteHandlerManager extends Handler  
{  
    function __construct($handlerName){  
        $this->_handlerName = $handlerName;  
    }  
      
    public function handleRequest($request)  
    {  
        if(0.5 <= $request->getDay() && $request->getDay()<=3) {  
            output(sprintf('%s signed', $this->_handlerName));       // 部门经理签字
            return $this->_success($request);  
        }   
        if ($this->_handler instanceof Handler) {  
            return $this->_handler->handleRequest($request);  
        }  
    }  

}  

class ConcreteHandlerGeneralManager extends Handler  
{  
    function __construct($handlerName){  
        $this->_handlerName = $handlerName;  
    }  
      
    public function handleRequest($request)  
    {  
        if(3 < $request->getDay() && $request->getDay() < 10){  
            output(sprintf('%s signed', $this->_handlerName));       // 总经理签字
            return $this->_success($request);  
        }  
        if ($this->_handler instanceof Handler) {  
            return $this->_handler->handleRequest($request);  
        } else {
            output(sprintf('no one can approve request more than 10 days'));
        }
    }  

}  

/** 
 * 请假申请 
 * 
 */  
class Request  
{  
    private $_name;  
    private $_day;  
    private $_reason;  
  
    function __construct($name= '', $day= 0, $reason = ''){  
        $this->_name = $name;  
        $this->_day = $day;  
        $this->_reason = $reason;  
    }  
      
    public function setName($name){  
        $this->_name = $name;  
    }  

    public function getName(){  
        return  $this->_name;  
    }  
      
    public function setDay($day){  
        $this->_day = $day;  
    }  

    public function getDay(){  
        return  $this->_day ;  
    }  
      
    public function setReason($reason ){  
         $this->_reason = $reason;  
    }  

    public function getReason( ){  
        return  $this->_reason;  
    }  
}  
  
  
class Client {  
      
    public static function test(){  
          
        $leader = new ConcreteHandlerLeader('leader');  
        $manager = new ConcreteHandlerManager('manager');  
        $generalManager = new ConcreteHandlerGeneralManager('generalManager');  
          
        //请求实例  
        $request = new Request('ucai',4,'休息');  
          
        $leader->setSuccessor($manager);  
        $manager->setSuccessor($generalManager);  
        $result =  $leader->handleRequest($request);  
    }  
      
}  
  
Client::test(); 

  

8、策略模式(Strategy) :

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

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

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

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

代码实现:

<?php

/**
 * 优才网公开课示例代码
 *
 * 策略模式 Strategy
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */


function output($string) {
    echo    $string . "\n";
}

//策略基类接口

interface IStrategy {
    public function OnTheWay();
}

class WalkStrategy implements IStrategy {
    public function OnTheWay() {
        output( '在路上步行');
    }
}

class RideBickStrategy implements IStrategy {
    public function OnTheWay() {
        output( '在路上骑自行车');
    }
}

class CarStrategy implements IStrategy {
    public function OnTheWay() {
        output( '在路上开车');
    }
}

//选择策略类Context
class Context {
    public function find($strategy) {
        $strategy->OnTheWay();
    }
}

class Client {  
      
    public static function test(){  

        $travel = new Context();
        $travel->find(new WalkStrategy());
        $travel->find(new RideBickStrategy());
        $travel->find(new CarStrategy());

    }  
      
}  
  
Client::test(); 

  

  • 已知模式

1、备忘录模式(Memento):

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

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

      弊端:消耗资源。

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

代码实现:

<?php

/**
 * 优才网公开课示例代码
 *
 * 备忘录模式 Memento
 *
 * @author 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */

function output($string) {
    echo    $string . "\n";
}


class Originator { // 发起人(Originator)角色
    private $_state;
    public function __construct() {
        $this->_state = '';
    }
    public function createMemento() { // 创建备忘录
        return new Memento($this->_state);
    }
    public function restoreMemento(Memento $memento) { // 将发起人恢复到备忘录对象记录的状态上
        $this->_state = $memento->getState();
    }
    public function setState($state) { $this->_state = $state; } 
    public function getState() { return $this->_state; }
    public function showState() {
        output($this->_state);
    }

}

class Memento { // 备忘录(Memento)角色 
    private $_state;
    public function __construct($state) {
        $this->setState($state);
    }
    public function getState() { return $this->_state; } 
    public function setState($state) { $this->_state = $state;}
}

class Caretaker { // 负责人(Caretaker)角色 
    private $_memento;
    public function getMemento() { return $this->_memento; } 
    public function setMemento(Memento $memento) { $this->_memento = $memento; }
}

class Client {  
      
    public static function test(){  

        $org = new Originator();
        $org->setState('open');
        $org->showState();

        /* 创建备忘 */
        $memento = $org->createMemento();

        /* 通过Caretaker保存此备忘 */
        $caretaker = new Caretaker();
        $caretaker->setMemento($memento);

        /* 改变目标对象的状态 */
        $org->setState('close');
        $org->showState();

        /* 还原操作 */
        $org->restoreMemento($caretaker->getMemento());
        $org->showState();

    }  
      
}  
  
Client::test(); 


return;


try {

    $db->beginTransaction();

    $succ   = $db->exec($sql_1);
    if (!$succ) {
        throw new Exception('SQL 1 update failed');
    }

    $succ   = $db->exec($sql_2);
    if (!$succ) {
        throw new Exception('SQL 2 update failed');
    }

    $succ   = $db->exec($sql_3);
    if (!$succ) {
        throw new Exception('SQL 3 update failed');
    }

    $db->commit();

} catch (Exception $exp) {

    $db->rollBack();

}

  

  • 深度模式

1、解释器模式(Interpreter):

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

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

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

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

2、访问者模式(Visitor):

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

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

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

      应用场景:排队,排号。

三、总结

      本篇介绍了行为型模式,行为模式涉及到算法和对象职责间的分配,行为类模式采用继承机制在类间分派行为,Template Method和Interpreter是类行为模式。行为对象模式使用对象复合而不是继承,一些行为

对象模式描述了一组相互对等的对象如何相互协作以完成其中任何一个对象都单独无法完成的任务,如Mediator在对象间引入一个mediator对象提供了松耦合所需的间接性;Chain of Responsibility提供了更松的耦

合,它通过一条候选对象链隐式的向一个对象发松请求,可以运行时刻决定哪些候选者参与到链中;Observer定义并保持了对象间的依赖关系;其它的行为对象模式常将行为封装封装在一个对象中,并将请求指派给

它,Strategy模式将算法封装在对象中,这样可以方面的改变和指定一个对象所使用的算法;Command模式将请求封装在对象中,这样它就可以作为参数来传递,已可以存储在历史列表中或以其它方式使用;State

模式封装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行为;Visitor模式封装分布于多个类之间的行为;而Iterator模式则抽象了访问和遍历一个集合中对象的方式。

 

posted @ 2014-05-29 17:19  混混人生  阅读(592)  评论(0编辑  收藏  举报