首页 > 其他分享 >【设计模式】观察者模式

【设计模式】观察者模式

时间:2024-06-06 09:35:29浏览次数:20  
标签:observer 观察者 模式 echo observers 设计模式 subject

设计模式

 

一、介绍

观察者模式是一种行为设计模式,当一个对象的状态发生改变时,依赖(观察)它的对象会接收到通知,并进行自动的更新操作。

举例:某公司发布了一款新的手机,性能很强大,许多人都想买,但是该公司又没宣布售卖时间。想买的人为了第一时间就拥有这台手机,就必须每天到官网或线下实体店看有没有出售,这样对于用户来说体验很不好。如果不想频繁的去查看,这时想买手机的用户就可以在实体店或网站上留下联系方式,等到手机出售的当天公司通过邮件或者短信的形式通知到购买者。

 

二、优缺点

优点:

  • 符合开闭原则。 无需修改发布者代码就能引入新的观察者类 。
  • 可以在运行时建立对象之间的联系。

缺点:

  • 无法设置订阅者收到的顺序
  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率

 

三、核心结构

  • 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、运行结果展示

 

标签:observer,观察者,模式,echo,observers,设计模式,subject
From: https://www.cnblogs.com/mklblog/p/18201411

相关文章

  • Vue 3 Composition API与Hooks模式
    Vue3的CompositionAPI引入了Hook函数的概念,这是一种更加模块化和可重用的状态管理和逻辑组织方式。自定义Hook首先,我们创建一个自定义Hook,例如useCounter,它封装了计数器的逻辑://useCounter.jsimport{ref}from'vue';exportfunctionuseCounter(){c......
  • SAP: SALV 利用控制器的模式
    SAP:SALV 例子利用控制器的模式利用控制器的SALV方法与实际业务中最常用的利用GRID显示ALV的方法其步骤一样。为了在画面上显示ALV事例,必须要存在连接画面和ALVGRID控件的SAP容器控件。SAP控制器为了充当LINKER将SAP控件包含于自己的领域。SAP控件包含SAPTree、SAPPIC......
  • 存储引擎及特点、约束条件、严格模式、基本字段类型(整型、浮点型、字符串、日期时间
    【一】存储引擎在平常我们处理的文件格式有很多,并且针对不同的文件格式会有对应不同的存储方式和处理机制针对不同的数据应该有对应不同的处理机制存储引擎就是不同的处理机制。#查看所有引擎showengines;四种主要的存储引擎(1)Innodb引擎是MySQL5.5版本之后的默认存......
  • 【设计模式】工厂模式(创建型)⭐⭐⭐
    文章目录1.概念1.1什么是工厂模式1.2优点与缺点2.实现方式2.1简单工厂模式(SimpleFactory)2.2简单工厂模式缺点2.3抽象工厂模式(AbstractFactoryPattern)3.Java哪些地方用到了工厂模式4.Spring哪些地方用到了工厂模式1.概念1.1什么是工厂模式工厂模式属......
  • python执行模式
    Python执行模式目录Python执行模式命令行模式交互模式好处:坏处:命令行模式命令行模式:写好命令之后,保存并运行整个文件。运行的时候,python解释器会一行一行对文件进行解析和执行。交互模式交互模式:输入一行后,python立即执行,并展示运行结果。好处:不需要创建任何新文件,py......
  • 基于阿里云服务网格流量泳道的全链路流量管理(三):无侵入式的宽松模式泳道
    作者:尹航在前文《基于阿里云服务网格流量泳道的全链路流量管理(一):严格模式流量泳道》、《基于阿里云服务网格流量泳道的全链路流量管理(二):宽松模式流量泳道》中,我们介绍了流量泳道的概念、使用流量泳道进行全链路灰度管理的方案,以及阿里云服务网格ASM提供的严格模式与宽松模式的......
  • 正则表达式学习(1)——模式
    正则表达式用于处理字符和字符串,是一种强大的工具1.正则表达式的模式字面值字符:例如字母、数字、空格等,可以直接匹配它们自身。特殊字符:例如点号.、星号*、加号+、问号?等,它们具有特殊的含义和功能。字符类:用方括号[]包围的字符集合,用于匹配方括号内的任......
  • 详解51种企业应用架构模式
    导读:企业应用包括哪些?它们又分别有哪些架构模式?世界著名软件开发大师MartinFowler给你答案一、什么是企业应用我的职业生涯专注于企业应用,因此,这里所谈及的模式也都是关于企业应用的。(企业应用还有一些其他的说法,如“信息系统”或更早期的“数据处理”。)那么,这里的“企业......
  • static vs Singleton,静态类vs单例模式之争
    https://stackoverflow.com/questions/519520/difference-between-static-class-and-singleton-pattern?answertab=modifieddesc#tab-top单例模式可以用接口,Singletoncanimplementinterface可以通过单例类来实现接口,但不能通过类的静态方法或者在某些语言(如C#)中的静态类来......
  • 巧用CMake编译策略:C++二次开发中的Release与Debug模式切换秘籍
    往期本博主的C++精讲优质博文可通过这篇导航进行查找:《Lemo的C++精华博文导航:进阶、精讲、设计模式文章全收录》前言在C++二次开发的过程中,理解各种编译模式并能灵活切换,对于提升软件性能和调试效率至关重要。本文将深入讨论Debug与Release模式的区别、默认编......