Php设计模式:行为型模式(二)

本文深入讲解了观察者模式、中介者模式及状态模式的应用场景、优缺点与代码实现,通过具体示例帮助读者理解这些模式如何简化软件设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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


<接上一篇>

4、观察者模式(Observer):

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

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

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

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

代码实现:

<?php

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

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


//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
    //订单号
    private $id = '';

    //用户ID
    private $userId = '';

    //用户名
    private $userName = '';

    //价格
    private $price = '';

    //下单时间
    private $orderTime = '';

    //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
    public function __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{
    public function save($data){
        return true;
    }
}


class Client {
    
    public static 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} was confirmed!" );

            //实际应用会调用邮件发送服务如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 优才网全栈工程师教研组
 * @see http://www.ucai.cn
 */

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


//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
    //订单号
    private $id = '';

    //用户ID
    private $userId = '';

    //用户名
    private $userName = '';

    //价格
    private $price = '';

    //下单时间
    private $orderTime = '';

    //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
    public function __set($name, $value){
        if (isset($this->$name)){
            $this->$name = $value;
        }
    }

    //获取订单属性
    public function __get($name){
        if (isset($this->$name)){
            return $this->$name;
        }
        return "";
    }
}

//被观察者, 负责维护观察者并在变化发生是通知观察者
class OrderSubject implements SplSubject {
    private $observers;
    private $order;

    public function __construct(Order $order) {
        $this->observers = new SplObjectStorage();
        $this->order = $order;
    }

    //增加一个观察者
    public function attach(SplObserver $observer) {
        $this->observers->attach($observer);
    }

    //移除一个观察者
    public function detach(SplObserver $observer) {
        $this->observers->detach($observer);
    }

    //通知所有观察者
    public function notify() {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    //返回主体对象的具体实现,供观察者调用
    public function getOrder() {
        return $this->order;
    }
}

//记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
class ActionLogObserver implements SplObserver{
    public function update(SplSubject $subject) {
         $order = $subject->getOrder();
         //实际应用可能会写到日志文件中,这里直接输出
         output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
    }
}

//给用户发送订单确认邮件 (UserMailObserver)
class UserMailObserver implements SplObserver{
    public function update(SplSubject $subject) {
         $order = $subject->getOrder();
         //实际应用会调用邮件发送服务如sendmail,这里直接输出
         output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
    }
}

//给管理人员发订单处理通知邮件 (AdminMailObserver)
class AdminMailObserver implements SplObserver{
    public function 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{
    public function save($data){
        return true;
    }
}


class Client {
    
    public static 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 = new UserMailObserver();
        $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 优才网全栈工程师教研组
 * @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();  

优才网免费公开课请戳这里: http://www.ucai.cn/course5/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值