php设计模式(三)-观察者模式

创建一个插件系统。通过这种方式,公司可以为了收益而继续开发自定义的专有软件,同时鼓励其他团体的成员借助该途径提供更多的功能性。由于只是插件系统,因此终端用户能够决定希望在核心软件包内添加的具体功能。在需要向代码库内添加新功能性而并不知道核心对象的时候,我们可以使用观察者模式。

观察者设计模式能够更便利的创建查看目标对象状态的对象,并且提供与核心对象非耦合的指定功能性。

插件系统是最显而易见的观察者设计模式示例。

为了保持切实有效,软件应用会不断要求添加新功能。如何在不必完全重构某些核心对象的情况下向应用程序添加额外的功能性?观察者设计模式设计了在最初创建对象时解决这个问题的蓝图。

首先,理解观察者设计模式基于状态变化十分重要。状态变化其实就是一个对象的变换。

为软件添加由某个动作或状态变化激活的、但是松散耦合的新功能时,就应当创建基于观察者设计模式的对象。

<?php
/*
 * 情景:本书的音乐web站点具有站点访问者可用的某些社交类型功能。其中集成的最新功能是一个活动流(activity stream),它
能够在主页上显示最近的购买情况。这种功能主要是希望人们点击最近出售的商品,从而使其可能购买相同的商品
 *
 * 1、将CD销售情况置入活动流的第一个步骤是创建一个基于观察者设计模式的CD对象
 * */
class CD
{
    public $title = '';
    public $band = '';
    //观察者保护数组
    protected $_observers = [];

    public function __construct($title, $band)
    {
        $this->title = $title;
        $this->band = $band;
    }

    /*
    接受一个观察者的实例并将其存储在观察者数组内
    第一个参数$type,因为CD对象可能存在许多状态变化类型,进一步指定通知类型
    第二个参数是将要添加入保护数组的观察者类
    */
    public function attachObserver($type, $observer)
    {
        $this->_observers[$type][] = $observer;
    }

    //对CD应用状态变化
    public function buy()
    {
        //stub actions of buying
        $this->notifyObserver('purchased');
    }

    /*
    遍历循环观察者数组。当在观察者数组中找到特定的观察者时,该方法会直接调用update方法
    根据$type访问相应的每个观察者。在这个方法中执行公共方法update()时,需要将指定
    对象的当前实例作为参数
    */
    public function notifyObserver($type)
    {
        if (isset($this->_observers[$type])) {
            foreach ($this->_observers[$type] as $observer)
            {
                $observer->update($this);
            }
        }
    }

}

/*
 * 2、随后要求某个观察者向活动流发布购买信息
 * */
class buyCDNotifyStreamObserver
{
    /*
    接受CD类的一个实例。这个特定的观察者接下来会对CD的内容执行某些操作。
    该方法只是从CD实例中收集信息并构建准备向活动流发布的内容
    */
    public function update(CD $cd)
    {
        $activity = "The CD named { $cd->title } by ";
        $activity .= "{ $cd->band } was just purchased. ";
        activityStream::addNewItem($activity);
    }
}

/*
 *
 * */
class activityStream
{
    /*这个方法还可能将$item参数写至数据库或可缓存的XML文件*/
    public static function addNewItem($item)
    {
        //stub functions
        print $item;
    }
}

//使用观察者启动CD销售的方式
$title = 'Waste of a Rib';
$band = 'Never Again';
$cd = new CD($title, $band);
//实例化一个新观察者实例,使用attachObserver()方法将该实例添加至CD对象。这个实例被规定为观察者的purchased类型
$observer = new buyCDNotifyStreamObserver();
$cd->attachObserver('purchased', $observer);
//指定CD对象被购买
$cd->buy();
?>

参考:https://blog.csdn.net/swengineer/article/details/6268244 

<?php

/**

 * 观察者模式应用场景实例


 * 免责声明:本文只是以哈票网举例,示例中并未涉及哈票网任何业务代码,全部原创,如有雷同,纯属巧合。

 *

 * 场景描述:

 * 哈票以购票为核心业务(此模式不限于该业务),但围绕购票会产生不同的其他逻辑,如:

 * 1、购票后记录文本日志

 * 2、购票后记录数据库日志

 * 3、购票后发送短信

 * 4、购票送抵扣卷、兑换卷、积分

 * 5、其他各类活动等

 *

 * 传统解决方案:

 * 在购票逻辑等类内部增加相关代码,完成各种逻辑。

 *

 * 存在问题:

 * 1、一旦某个业务逻辑发生改变,如购票业务中增加其他业务逻辑,需要修改购票核心文件、甚至购票流程。

 * 2、日积月累后,文件冗长,导致后续维护困难。

 *

 * 存在问题原因主要是程序的"紧密耦合",使用观察模式将目前的业务逻辑优化成"松耦合",达到易维护、易修改的目的,

 * 同时也符合面向接口编程的思想。

 *

 * 观察者模式典型实现方式:

 * 1、定义2个接口:观察者(通知)接口、被观察者(主题)接口

 * 2、定义2个类,观察者对象实现观察者接口、主题类实现被观者接口

 * 3、主题类注册自己需要通知的观察者

 * 4、主题类某个业务逻辑发生时通知观察者对象,每个观察者执行自己的业务逻辑。

 *

 * 示例:如以下代码

 *

 */

#===================定义观察者、被观察者接口============

/**

 * 观察者接口(通知接口)

 */

interface ITicketObserver //观察者接口

{

    function onBuyTicketOver($sender, $args); //得到通知后调用的方法

}

 

/**

 * 主题接口

 */

interface ITicketObservable //被观察对象接口

{

    function addObserver($observer); //提供注册观察者方法

}

#====================主题类实现========================

/**

 * 主题类(购票)

 */

class HipiaoBuy implements ITicketObservable { //实现主题接口(被观察者)

    private $_observers = array (); //通知数组(观察者)

    public function buyTicket($ticket) //购票核心类,处理购票流程

    {

       // TODO 购票逻辑

       //循环通知,调用其onBuyTicketOver实现不同业务逻辑

       foreach ( $this->_observers as $obs )

           $obs->onBuyTicketOver ( $this, $ticket ); //$this 可用来获取主题类句柄,在通知中使用

    }

   

    //添加通知

    public function addObserver($observer) //添加N个通知

    {

       $this->_observers [] = $observer;

    }

}

 

#=========================定义多个通知====================

//短信日志通知

class HipiaoMSM implements ITicketObserver {

    public function onBuyTicketOver($sender, $ticket) {

       echo (date ( 'Y-m-d H:i:s' ) . " 短信日志记录:购票成功:$ticket<br>");

    }

}

//文本日志通知

class HipiaoTxt implements ITicketObserver {

    public function onBuyTicketOver($sender, $ticket) {

       echo (date ( 'Y-m-d H:i:s' ) . " 文本日志记录:购票成功:$ticket<br>");

    }

}

//抵扣卷赠送通知

class HipiaoDiKou implements ITicketObserver {

    public function onBuyTicketOver($sender, $ticket) {

       echo (date ( 'Y-m-d H:i:s' ) . " 赠送抵扣卷:购票成功:$ticket 赠送10元抵扣卷1张。<br>");

    }

}

#============================用户购票====================

$buy = new HipiaoBuy ();

$buy->addObserver ( new HipiaoMSM () ); //根据不同业务逻辑加入各种通知

$buy->addObserver ( new HipiaoTxt () );

$buy->addObserver ( new HipiaoDiKou () );

//购票

$buy->buyTicket ( "一排一号" );


?>

猜你喜欢

转载自blog.csdn.net/hrbsfdxzhq01/article/details/88848721
今日推荐