【设计模式】观察者模式
设计模式
一、介绍
观察者模式是一种行为设计模式,当一个对象的状态发生改变时,依赖(观察)它的对象会接收到通知,并进行自动的更新操作。
举例:某公司发布了一款新的手机,性能很强大,许多人都想买,但是该公司又没宣布售卖时间。想买的人为了第一时间就拥有这台手机,就必须每天到官网或线下实体店看有没有出售,这样对于用户来说体验很不好。如果不想频繁的去查看,这时想买手机的用户就可以在实体店或网站上留下联系方式,等到手机出售的当天公司通过邮件或者短信的形式通知到购买者。
二、优缺点
优点:
- 符合开闭原则。 无需修改发布者代码就能引入新的观察者类 。
- 可以在运行时建立对象之间的联系。
缺点:
- 无法设置订阅者收到的顺序
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
三、核心结构
- Subject(目标):被观察者,它是指被观察的对象。 类中有一个用来存放观察者对象的容器,这个容器是被观察者类的核心。其中还有几个方法:
- attach方法是向这个容器中添加观察者对象。
- detach方法是从容器中移除观察者对象。
- notify方法是依次调用观察者对象的对应方法。
- ConcreteSubject(具体目标):目标类的具体子类,当它的状态发生改变时,向它的各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。
- Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法 update()。
- ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致,它实现了在观察者 Observer 中定义的 update()方法。
四、代码实现
1、在PHP中已经有相关的Subject(目标)和Observer(观察者)接口了,我们可以拿来直接实现。分别是SplSubject和SplObserver接口,以下代码就是以这两个接口为例进行编写。其中还用到一个SplObjectStorage类,它也是PHP中的一个类,用于存储和管理对象。它是一个关联数组,其中键是对象的哈希值,值是对象本身。
1.1、实现ConcreteSubject(具体目标)
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/17 * Time: 10:43 */ namespace app\admin\service\mode\observers; use SplObserver; /** * 观察者模式 * 使用PHP自带的观察者设计模式 */ class ObserversService implements \SplSubject { public int $status; private $observers; public function __construct() { $this->observers = new \SplObjectStorage(); } /** * 添加观察者 * @param SplObserver $observer * @return void * @Author: fengzi * @Date: 2024/5/20 17:50 */ public function attach(SplObserver $observer) { // TODO: Implement attach() method. echo "添加一个观察者\n"; $this->observers->attach($observer); } /** * 删除观察者 * @param SplObserver $observer * @return void * @Author: fengzi * @Date: 2024/5/20 17:50 */ public function detach(SplObserver $observer) { // TODO: Implement detach() method. echo "\n分离一个观察者\n"; $this->observers->detach($observer); } /** * 通知观察者 * @return void * @Author: fengzi * @Date: 2024/5/20 17:51 */ public function notify() { // TODO: Implement notify() method. echo "已通知观察者\n"; foreach ($this->observers as $observer) { $observer->update($this); } } /** * 实现被观察者业务,并通知观察者 * @return void * @Author: fengzi * @Date: 2024/5/20 17:51 */ public function doSomeLogic(): void { echo "\nSubject: 我做了一些业务...\n"; $this->status = rand(0, 10); echo "Subject: 业务状态小于5时观察者才做出反应,当前业务状态: {$this->status}\n"; $this->notify(); } }
1.2、实现ConcreteObserver(具体观察者),我这里实现了两个观察者,分别为 ConcreteObserverB 和 ConcreteObserverA 。
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/17 * Time: 10:54 */ namespace app\admin\service\mode\observers; use SplSubject; class ConcreteObserverB implements \SplObserver { public function update(SplSubject $subject) { // TODO: Implement update() method. if ($subject->status < 5) { echo "ConcreteObserverB: 我接受到状态,并做出了相应的反应。\n"; } } }
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/17 * Time: 10:54 */ namespace app\admin\service\mode\observers; use SplSubject; class ConcreteObserverA implements \SplObserver { public function update(SplSubject $subject) { // TODO: Implement update() method. if ($subject->status < 5) { echo "ConcreteObserverA: 我接受到状态,并做出了相应的反应。\n"; } } }
1.3、客户端调用
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/17 * Time: 10:34 */ namespace app\admin\controller\mode\observers; use app\admin\service\mode\observers\ConcreteObserverA; use app\admin\service\mode\observers\ConcreteObserverB; use app\admin\service\mode\observers\ObserversService; /** * 观察者模式客户端调用 */ class ObserversController { public function index() { // 创建被观察者 $subject = new ObserversService(); // 创建观察者 $obA = new ConcreteObserverA(); $obB = new ConcreteObserverB(); // 注册观察者 $subject->attach($obA); $subject->attach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomeLogic(); // 移除观察者 $subject->detach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomeLogic(); dd('结束'); } }
1.4、客户端调用结果展示
2、上面介绍了使用PHP本身观察者设计模式的接口,下面就自己手写一个观察者模式。
2.1、实现Subject(目标)接口
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/21 * Time: 10:52 */ namespace app\admin\service\mode\observers\my; /** * 被观察者接口 */ interface Subject { /** * 添加观察者对象 * @param Observer $observer 观察者对象 * @return mixed * @Author: fengzi * @Date: 2024/5/21 10:56 */ public function attach(Observer $observer); /** * 删除观察者对象 * @param Observer $observer 观察者对象 * @return mixed * @Author: fengzi * @Date: 2024/5/21 10:56 */ public function detach(Observer $observer); /** * 通知观察者 * @return mixed * @Author: fengzi * @Date: 2024/5/21 10:54 */ public function notify(); }
2.2、实现ConcreteSubject(具体目标)
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/21 * Time: 10:54 */ namespace app\admin\service\mode\observers\my; class ConcreteSubject implements Subject { public int $status; private array $observers = []; public function attach(Observer $observer) { // TODO: Implement attach() method. echo "添加一个观察者\n"; if ( !in_array($observer, $this->observers, true) ) { $this->observers[] = $observer; } } public function detach(Observer $observer) { // TODO: Implement detach() method. echo "\n分离一个观察者:".get_class($observer)."\n"; if ( in_array($observer, $this->observers, true) ) { unset($this->observers[array_search($observer, $this->observers, true)]); } } public function notify() { // TODO: Implement notify() method. echo "已通知观察者\n"; foreach ($this->observers as $observer) { $observer->update($this); } } public function doSomething() { echo "\nSubject: 我做了一些业务...\n"; $this->status = rand(0, 10); echo "Subject: 业务状态小于5时观察者才做出反应,当前业务状态: {$this->status}\n"; $this->notify(); } }
2.3、实现Observer(观察者)
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/21 * Time: 10:57 */ namespace app\admin\service\mode\observers\my; /** * 观察者接口 */ interface Observer { public function update(Subject $subject); }
2.4、实现ConcreteObserver(具体观察者),分别为 ConcreteObserverB 和 ConcreteObserverA 。
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/21 * Time: 10:58 */ namespace app\admin\service\mode\observers\my; /** * 具体观察者A */ class ConcreteObserverA implements Observer { public function update(Subject $subject) { // TODO: Implement update() method. if ($subject->status < 5) { echo "ConcreteObserverA: 我接受到状态,并做出了相应的反应。\n"; } } }
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/21 * Time: 11:06 */ namespace app\admin\service\mode\observers\my; class ConcreteObserverB implements Observer { public function update(Subject $subject) { // TODO: Implement update() method. if ($subject->status < 5) { echo "ConcreteObserverB: 我接受到状态,并做出了相应的反应。\n"; } } }
2.5、客户端调用
<?php /** * Created by PhpStorm * Author: fengzi * Date: 2024/5/17 * Time: 10:34 */ namespace app\admin\controller\mode\observers; use app\admin\service\mode\observers\ConcreteObserverA; use app\admin\service\mode\observers\ConcreteObserverB; use app\admin\service\mode\observers\my\ConcreteSubject; use app\admin\service\mode\observers\ObserversService; /** * 观察者模式客户端调用 */ class ObserversController { /** * 使用PHP自带的观察者模式 * @return void * @Author: fengzi * @Date: 2024/5/21 11:18 */ public function index() { // 创建被观察者 $subject = new ObserversService(); // 创建观察者 $obA = new ConcreteObserverA(); $obB = new ConcreteObserverB(); // 注册观察者 $subject->attach($obA); $subject->attach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomeLogic(); // 移除观察者 $subject->detach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomeLogic(); dd('结束'); } /** * 使用自定义的观察者模式 * @return void * @Author: fengzi * @Date: 2024/5/21 11:18 */ public function mySubject() { // 创建被观察者 $subject = new ConcreteSubject(); // 创建观察者 $obA = new \app\admin\service\mode\observers\my\ConcreteObserverA(); $obB = new \app\admin\service\mode\observers\my\ConcreteObserverB(); // 注册观察者 $subject->attach($obA); $subject->attach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomething(); // 移除观察者 $subject->detach($obB); // 被观察者执行业务逻辑并通知给观察者 $subject->doSomething(); dd('结束'); } }
2.6、运行结果展示
本文来自博客园,作者:疯子丶pony,转载请注明原文链接:https://www.cnblogs.com/mklblog/p/18201411