观察者模式

观察者模式

观察者模式又称发布订阅模式,我们常用的redisrabbitmqkafka等都支持发布订阅,那么这个模式是怎么回事儿呢?

观察者模式主要有通知者和观察者等角色,观察者一般有多个。

我们看下面代码示例

<?php
/**
 * Class Controller
 * @datetime 2020/7/17 6:46 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Controller
{
    /**
     * @var string
     * @datetime 2020/7/17 6:36 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $id;

    /**
     * @var string
     * @datetime 2020/7/17 6:39 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $actionId;

    /**
     * @datetime 2020/7/17 6:46 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function init()
    {
        $userId = time();
        
        $this->checkAccess($userId);
        $this->trafficLimit();
    }

    /**鉴权
     * @param int $userId
     * @return int
     * @datetime 2020/7/17 6:41 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function checkAccess($userId)
    {
        return $userId & 1=== 1;
    }

    /**
     * @return bool
     * @datetime 2020/7/17 6:45 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function trafficLimit()
    {
        $ip = $_SERVER['REMOTE_ADDR'];
        /**
         * @var \Redis $redis
         */
        $multi = $redis->multi(\Redis::PIPELINE);
        $limitKey = 'limit:'.$ip;
        $multi->incr($limitKey);
        $multi->expire($limitKey, 60);
        $result = $multi->exec();
        
        return $result[0] > 100;
    }
}

以上代码是一个控制器基类,当有请求来临时会优先调用控制器的init方法,init方法会主动调用checkAccesstrafficLimit方法,一个用于鉴权,一个用于限速,这段代码在我们日常项目中也是非常常见,那么有什么问题呢?

现在我们通过开闭原则进行分析,当我们修改限速逻辑,我们需要修改trafficLimit方法,Controller发生了变化,没有对修改关闭;当我们增加其他调用时,init方法需要发生修改,没有对扩展开放,那么我们怎么修改呢?

<?php
/**
 * Interface IFilter
 * @datetime 2020/7/17 6:52 PM
 * @author roach
 * @email jhq0113@163.com
 */
interface IFilter
{
    /**
     * @param array $params
     * @return mixed
     * @datetime 2020/7/17 6:51 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function filter($params = []);
}

/**
 * Class AccessFilter
 * @datetime 2020/7/17 6:52 PM
 * @author roach
 * @email jhq0113@163.com
 */
class AccessFilter implements IFilter
{
    /**
     * @param array $params
     * @return int|mixed
     * @datetime 2020/7/17 6:52 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function filter($params = [])
    {
        return $params['userId'] & 1=== 1;
    }
}

/**
 * Class LimitFilter
 * @datetime 2020/7/17 6:53 PM
 * @author roach
 * @email jhq0113@163.com
 */
class LimitFilter implements IFilter
{
    /**
     * @param array $params
     * @return bool|mixed
     * @datetime 2020/7/17 6:53 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function filter($params = [])
    {
        $ip = $_SERVER['REMOTE_ADDR'];
        /**
         * @var \Redis $redis
         */
        $multi = $redis->multi(\Redis::PIPELINE);
        $limitKey = 'limit:'.$ip;
        $multi->incr($limitKey);
        $multi->expire($limitKey, 60);
        $result = $multi->exec();

        return $result[0] > 100;
    }
}

/**
 * Class Controller
 * @datetime 2020/7/17 6:46 PM
 * @author roach
 * @email jhq0113@163.com
 */
class Controller
{
    /**
     * @var string
     * @datetime 2020/7/17 6:36 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $id;

    /**
     * @var string
     * @datetime 2020/7/17 6:39 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public $actionId;

    /**
     * @var array
     * @datetime 2020/7/17 6:51 PM
     * @author roach
     * @email jhq0113@163.com 
     */
    protected $_filters = [];
    
    /**
     * @datetime 2020/7/17 6:46 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function init()
    {
        $this->notify();
    }

    /**
     * @param IFilter $filter
     * @datetime 2020/7/17 6:55 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function addFilter(IFilter $filter)
    {
        array_push($this->_filters, $filter);
    }
    
    /**
     * @datetime 2020/7/17 6:55 PM
     * @author roach
     * @email jhq0113@163.com
     */
    public function notify()
    {
        $params = [
            'userId' => time()
        ];
        
        foreach ($this->_filters as $filter) {
            /**
             * @var IFilter $filter
             */
            $filter->filter($params);
        }
    }
}

修改之后,我们增加了IFilter接口(即抽象观察者角色),增加了AccessFilter(即具体观察者角色)和LimitFilter(即具体观察者角色)两个实现类,在控制器(即通知者角色)中增加了_filters属性,控制器可以通过调用addFilter方法增加观察者,通过调用notify方法通知所有观察者。

我们来分析一下修改后的优点,当我们要修改鉴权或者限速的逻辑时,控制器的代码是不需要发生修改的,属于对修改关闭;当我们要增加新的filter时,增加一个类就可以了,控制器的代码也不用发生修改,属于对扩展开放。

一个对象必须通知其他对象,而并不知道这些对象是谁。需要在系统中创建一个触发链,这时我们就可以使用观察者模式来解决。

作者开源了一个事件的实现,封装在了jhq0113/roach中,我们可以通过composer方式安装,代码开源地址如下

https://github.com/jhq0113/roach

jhq0113/roach使用简单,代码精简,整个代码库纯代码大小为60K,composer安装方式

composer require jhq0113/roach

学习更多内容: https://404.360tryst.com

我的视频课程: https://edu.csdn.net/course/detail/9933

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍穹0113

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值