php设计模式(十九):观察者模式(Observer)

2023-05-22 08:35:21 浏览数 (1)

观察者模式

观察者模式又称为:事件订阅者、监听者、Event-Subscriber、Listener、Observer。观察者是一种行为设计模式,允许定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。

问题

观察者模式是一种发布和订阅模式,我们平常去抢购茅台、Aj、…多少会用到观察者模式。

如我们抢购鞋子(AJ),平常每天去门店看看鞋子是否到货。但如果鞋子尚未到货时,绝大多数来到 门店 的顾客都会空手而归。

另一方面,每次新鞋子到货时,门店可以向所有顾客发送邮件(可能会被视为垃圾邮件)。 这样, 部分顾客就无需反复前往门店了,也可能会惹恼对新鞋子没有兴趣的其他顾客。

那么我们遇到了一个矛盾:要么让顾客浪费时间检查产品是否到货,要么让商店浪费资源去通知没有需求的顾客。

解决方法

拥有一些值得关注的状态的对象通常被称为 目标,由于它要将自身的状态改变通知给其他对象,我们也将其称为 发布者(publisher)。所有希望关注发布者状态变化的其他对象被称为 订阅者(subscribers)。

我们为发布者类(门店)添加订阅机制,让每个对象(顾客)都能订阅或取消订阅发布者事件流。

该机制包括:

  1. 一个用于存储订阅者对象引用的列表成员变量;
  2. 几个用于添加或删除该列表中订阅者的公有方法。

结构

Shop: 具体的门店类; Subscriber: 订阅者接口 ; Customer: 具体的 Subscriber 示例中指 顾客 ;

代码示例

具体的门店类(Shop 类)

代码语言:javascript复制
/**
 * 门店
 */
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();
        }
    }
}

订阅者接口

代码语言:javascript复制
abstract class Subscriber
{
    /**
     * @var string 订阅者姓名
     */
    public string $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    abstract public function update();
}

顾客类

代码语言:javascript复制
class Customer extends Subscriber
{
    public function update()
    {
        echo '我是' . $this->name, ',我马上来门店产看新产品' . PHP_EOL;
    }
}

客户端使用

代码语言:javascript复制
$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();

输出

代码语言:javascript复制
我是张三,我马上来门店产看新产品
我是李四,我马上来门店产看新产品
我是王五,我马上来门店产看新产品

产品不好,李四想退出订阅
我是张三,我马上来门店产看新产品
我是王五,我马上来门店产看新产品

php 官方已经提供了,请参考:SplObserver 和 SplSubject

UML

优缺点

优点

  • 开闭原则。 无需修改发布者代码就能引入新的订阅者类(如果是发布者接口则可轻松引入发布者类)。
  • 可以在运行时建立对象之间的联系。

缺点

  • 订阅者的通知顺序是随机的。

0 人点赞