观察者模式
观察者模式又称发布订阅模式,我们常用的
redis、rabbitmq和kafka等都支持发布订阅,那么这个模式是怎么回事儿呢?
观察者模式主要有通知者和观察者等角色,观察者一般有多个。
我们看下面代码示例
<?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方法会主动调用checkAccess和trafficLimit方法,一个用于鉴权,一个用于限速,这段代码在我们日常项目中也是非常常见,那么有什么问题呢?
现在我们通过开闭原则进行分析,当我们修改限速逻辑,我们需要修改
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
1571

被折叠的 条评论
为什么被折叠?



