php设计模式(十九):观察者模式(Observer)
- 陈大剩
- 2023-05-21 20:21:43
- 1063
观察者模式
观察者模式又称为:事件订阅者、监听者、Event-Subscriber、Listener、Observer。观察者是一种行为设计模式,允许定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。
问题
观察者模式是一种发布和订阅模式,我们平常去抢购茅台、Aj、…多少会用到观察者模式。
如我们抢购鞋子(AJ),平常每天去门店看看鞋子是否到货。但如果鞋子尚未到货时,绝大多数来到 门店 的顾客都会空手而归。
另一方面,每次新鞋子到货时,门店可以向所有顾客发送邮件(可能会被视为垃圾邮件)。 这样, 部分顾客就无需反复前往门店了,也可能会惹恼对新鞋子没有兴趣的其他顾客。
那么我们遇到了一个矛盾:要么让顾客浪费时间检查产品是否到货,要么让商店浪费资源去通知没有需求的顾客。
解决方法
拥有一些值得关注的状态的对象通常被称为 目标,由于它要将自身的状态改变通知给其他对象,我们也将其称为 发布者(publisher)。所有希望关注发布者状态变化的其他对象被称为 订阅者(subscribers)。
我们为发布者类(门店)添加订阅机制,让每个对象(顾客)都能订阅或取消订阅发布者事件流。
该机制包括:
- 一个用于存储订阅者对象引用的列表成员变量;
- 几个用于添加或删除该列表中订阅者的公有方法。
结构
Shop: 具体的门店类;
Subscriber: 订阅者接口 ;
Customer: 具体的 Subscriber 示例中指 顾客 ;
代码示例
具体的门店类(Shop 类)
/**
* 门店
*/
class Shop
{
/**
* 订阅者
* @var array
*/
private $subscribers = [];
/**
* 添加订阅者
* @return void
* @author chendashengpc
*/
public function attach(Subscriber $subscriber)
{
$this->subscribers[$subscriber->name] = $subscriber;
}
/**
* 删除订阅者
* @return void
* @author chendashengpc
*/
public function detach(Subscriber $subscriber)
{
if (isset($this->subscribers[$subscriber->name])) {
unset($this->subscribers[$subscriber->name]);
}
}
/**
* 提醒
* @return void
* @author chendashengpc
*/
public function notify()
{
if (count($this->subscribers) == 0) {
return;
}
foreach ($this->subscribers as $subscriber) {
$subscriber->update();
}
}
}
订阅者接口
abstract class Subscriber
{
/**
* @var string 订阅者姓名
*/
public string $name;
public function __construct($name)
{
$this->name = $name;
}
abstract public function update();
}
顾客类
class Customer extends Subscriber
{
public function update()
{
echo '我是' . $this->name, ',我马上来门店产看新产品' . PHP_EOL;
}
}
客户端使用
$shop = new Shop();
$zs = new Customer('张三');
$ls = new Customer('李四');
$ww = new Customer('王五');
$shop->attach($zs);
$shop->attach($ls);
$shop->attach($ww);
$shop->notify();
echo PHP_EOL;
echo '产品不好,李四想退出订阅' . PHP_EOL;
$shop->detach($ls);
$shop->notify();
输出
我是张三,我马上来门店产看新产品
我是李四,我马上来门店产看新产品
我是王五,我马上来门店产看新产品
产品不好,李四想退出订阅
我是张三,我马上来门店产看新产品
我是王五,我马上来门店产看新产品
php 官方已经提供了,请参考:SplObserver 和 SplSubject
UML
优缺点
优点
- 开闭原则。 无需修改发布者代码就能引入新的订阅者类(如果是发布者接口则可轻松引入发布者类)。
- 可以在运行时建立对象之间的联系。
缺点
- 订阅者的通知顺序是随机的。