当前位置:   article > 正文

【设计模式】观察者模式

【设计模式】观察者模式

一、介绍

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

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

二、优缺点

优点:

  • 符合开闭原则。 无需修改发布者代码就能引入新的观察者类 。

  • 可以在运行时建立对象之间的联系。

缺点:

  • 无法设置订阅者收到的顺序

  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率

三、核心结构

  • Subject(目标):被观察者,它是指被观察的对象。 类中有一个用来存放观察者对象的容器,这个容器是被观察者类的核心。其中还有几个方法:attach方法是向这个容器中添加观察者对象。detach方法是从容器中移除观察者对象。notify方法是依次调用观察者对象的对应方法。

  • ConcreteSubject(具体目标):目标类的具体子类,当它的状态发生改变时,向它的各个观察者发出通知。同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。

  • Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法 update()。

  • ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致,它实现了在观察者 Observer 中定义的 update()方法。

四、代码实现

1、在PHP中已经有相关的Subject(目标)和Observer(观察者)接口了,我们可以拿来直接实现。分别是SplSubject和SplObserver接口,以下代码就是以这两个接口为例进行编写。其中还用到一个SplObjectStorage类,它也是PHP中的一个类,用于存储和管理对象。它是一个关联数组,其中键是对象的哈希值,值是对象本身。

1.1、实现ConcreteSubject(具体目标)

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/17
  6. * Time: 10:43
  7. */
  8. namespace app\admin\service\mode\observers;
  9. use SplObserver;
  10. /**
  11. * 观察者模式
  12. * 使用PHP自带的观察者设计模式
  13. */
  14. class ObserversService implements \SplSubject
  15. {
  16. public int $status;
  17. private $observers;
  18. public function __construct()
  19. {
  20. $this->observers = new \SplObjectStorage();
  21. }
  22. /**
  23. * 添加观察者
  24. * @param SplObserver $observer
  25. * @return void
  26. * @Author: fengzi
  27. * @Date: 2024/5/20 17:50
  28. */
  29. public function attach(SplObserver $observer)
  30. {
  31. // TODO: Implement attach() method.
  32. echo "添加一个观察者\n";
  33. $this->observers->attach($observer);
  34. }
  35. /**
  36. * 删除观察者
  37. * @param SplObserver $observer
  38. * @return void
  39. * @Author: fengzi
  40. * @Date: 2024/5/20 17:50
  41. */
  42. public function detach(SplObserver $observer)
  43. {
  44. // TODO: Implement detach() method.
  45. echo "\n分离一个观察者\n";
  46. $this->observers->detach($observer);
  47. }
  48. /**
  49. * 通知观察者
  50. * @return void
  51. * @Author: fengzi
  52. * @Date: 2024/5/20 17:51
  53. */
  54. public function notify()
  55. {
  56. // TODO: Implement notify() method.
  57. echo "已通知观察者\n";
  58. foreach ($this->observers as $observer) {
  59. $observer->update($this);
  60. }
  61. }
  62. /**
  63. * 实现被观察者业务,并通知观察者
  64. * @return void
  65. * @Author: fengzi
  66. * @Date: 2024/5/20 17:51
  67. */
  68. public function doSomeLogic(): void
  69. {
  70. echo "\nSubject: 我做了一些业务...\n";
  71. $this->status = rand(0, 10);
  72. echo "Subject: 业务状态小于5时观察者才做出反应,当前业务状态: {$this->status}\n";
  73. $this->notify();
  74. }
  75. }

1.2、实现ConcreteObserver(具体观察者),我这里实现了两个观察者,分别为 ConcreteObserverB 和 ConcreteObserverA 。

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/17
  6. * Time: 10:54
  7. */
  8. namespace app\admin\service\mode\observers;
  9. use SplSubject;
  10. class ConcreteObserverB implements \SplObserver
  11. {
  12. public function update(SplSubject $subject)
  13. {
  14. // TODO: Implement update() method.
  15. if ($subject->status < 5) {
  16. echo "ConcreteObserverB: 我接受到状态,并做出了相应的反应。\n";
  17. }
  18. }
  19. }

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/17
  6. * Time: 10:54
  7. */
  8. namespace app\admin\service\mode\observers;
  9. use SplSubject;
  10. class ConcreteObserverA implements \SplObserver
  11. {
  12. public function update(SplSubject $subject)
  13. {
  14. // TODO: Implement update() method.
  15. if ($subject->status < 5) {
  16. echo "ConcreteObserverA: 我接受到状态,并做出了相应的反应。\n";
  17. }
  18. }
  19. }

1.3、客户端调用

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/17
  6. * Time: 10:34
  7. */
  8. namespace app\admin\controller\mode\observers;
  9. use app\admin\service\mode\observers\ConcreteObserverA;
  10. use app\admin\service\mode\observers\ConcreteObserverB;
  11. use app\admin\service\mode\observers\ObserversService;
  12. /**
  13. * 观察者模式客户端调用
  14. */
  15. class ObserversController
  16. {
  17. public function index()
  18. {
  19. // 创建被观察者
  20. $subject = new ObserversService();
  21. // 创建观察者
  22. $obA = new ConcreteObserverA();
  23. $obB = new ConcreteObserverB();
  24. // 注册观察者
  25. $subject->attach($obA);
  26. $subject->attach($obB);
  27. // 被观察者执行业务逻辑并通知给观察者
  28. $subject->doSomeLogic();
  29. // 移除观察者
  30. $subject->detach($obB);
  31. // 被观察者执行业务逻辑并通知给观察者
  32. $subject->doSomeLogic();
  33. dd('结束');
  34. }
  35. }

1.4、客户端调用结果展示

2、上面介绍了使用PHP本身观察者设计模式的接口,下面就自己手写一个观察者模式。

2.1、实现Subject(目标)接口

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/21
  6. * Time: 10:52
  7. */
  8. namespace app\admin\service\mode\observers\my;
  9. /**
  10. * 被观察者接口
  11. */
  12. interface Subject
  13. {
  14. /**
  15. * 添加观察者对象
  16. * @param Observer $observer 观察者对象
  17. * @return mixed
  18. * @Author: fengzi
  19. * @Date: 2024/5/21 10:56
  20. */
  21. public function attach(Observer $observer);
  22. /**
  23. * 删除观察者对象
  24. * @param Observer $observer 观察者对象
  25. * @return mixed
  26. * @Author: fengzi
  27. * @Date: 2024/5/21 10:56
  28. */
  29. public function detach(Observer $observer);
  30. /**
  31. * 通知观察者
  32. * @return mixed
  33. * @Author: fengzi
  34. * @Date: 2024/5/21 10:54
  35. */
  36. public function notify();
  37. }

2.2、实现ConcreteSubject(具体目标)

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/21
  6. * Time: 10:54
  7. */
  8. namespace app\admin\service\mode\observers\my;
  9. class ConcreteSubject implements Subject
  10. {
  11. public int $status;
  12. private array $observers = [];
  13. public function attach(Observer $observer)
  14. {
  15. // TODO: Implement attach() method.
  16. echo "添加一个观察者\n";
  17. if ( !in_array($observer, $this->observers, true) ) {
  18. $this->observers[] = $observer;
  19. }
  20. }
  21. public function detach(Observer $observer)
  22. {
  23. // TODO: Implement detach() method.
  24. echo "\n分离一个观察者:".get_class($observer)."\n";
  25. if ( in_array($observer, $this->observers, true) ) {
  26. unset($this->observers[array_search($observer, $this->observers, true)]);
  27. }
  28. }
  29. public function notify()
  30. {
  31. // TODO: Implement notify() method.
  32. echo "已通知观察者\n";
  33. foreach ($this->observers as $observer) {
  34. $observer->update($this);
  35. }
  36. }
  37. public function doSomething()
  38. {
  39. echo "\nSubject: 我做了一些业务...\n";
  40. $this->status = rand(0, 10);
  41. echo "Subject: 业务状态小于5时观察者才做出反应,当前业务状态: {$this->status}\n";
  42. $this->notify();
  43. }
  44. }

2.3、实现Observer(观察者)

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/21
  6. * Time: 10:57
  7. */
  8. namespace app\admin\service\mode\observers\my;
  9. /**
  10. * 观察者接口
  11. */
  12. interface Observer
  13. {
  14. public function update(Subject $subject);
  15. }

2.4、实现ConcreteObserver(具体观察者),分别为 ConcreteObserverB 和 ConcreteObserverA 。

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/21
  6. * Time: 10:58
  7. */
  8. namespace app\admin\service\mode\observers\my;
  9. /**
  10. * 具体观察者A
  11. */
  12. class ConcreteObserverA implements Observer
  13. {
  14. public function update(Subject $subject)
  15. {
  16. // TODO: Implement update() method.
  17. if ($subject->status < 5) {
  18. echo "ConcreteObserverA: 我接受到状态,并做出了相应的反应。\n";
  19. }
  20. }
  21. }

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/21
  6. * Time: 11:06
  7. */
  8. namespace app\admin\service\mode\observers\my;
  9. class ConcreteObserverB implements Observer
  10. {
  11. public function update(Subject $subject)
  12. {
  13. // TODO: Implement update() method.
  14. if ($subject->status < 5) {
  15. echo "ConcreteObserverB: 我接受到状态,并做出了相应的反应。\n";
  16. }
  17. }
  18. }

2.5、客户端调用

  1. <?php
  2. /**
  3. * Created by PhpStorm
  4. * Author: fengzi
  5. * Date: 2024/5/17
  6. * Time: 10:34
  7. */
  8. namespace app\admin\controller\mode\observers;
  9. use app\admin\service\mode\observers\ConcreteObserverA;
  10. use app\admin\service\mode\observers\ConcreteObserverB;
  11. use app\admin\service\mode\observers\my\ConcreteSubject;
  12. use app\admin\service\mode\observers\ObserversService;
  13. /**
  14. * 观察者模式客户端调用
  15. */
  16. class ObserversController
  17. {
  18. /**
  19. * 使用PHP自带的观察者模式
  20. * @return void
  21. * @Author: fengzi
  22. * @Date: 2024/5/21 11:18
  23. */
  24. public function index()
  25. {
  26. // 创建被观察者
  27. $subject = new ObserversService();
  28. // 创建观察者
  29. $obA = new ConcreteObserverA();
  30. $obB = new ConcreteObserverB();
  31. // 注册观察者
  32. $subject->attach($obA);
  33. $subject->attach($obB);
  34. // 被观察者执行业务逻辑并通知给观察者
  35. $subject->doSomeLogic();
  36. // 移除观察者
  37. $subject->detach($obB);
  38. // 被观察者执行业务逻辑并通知给观察者
  39. $subject->doSomeLogic();
  40. dd('结束');
  41. }
  42. /**
  43. * 使用自定义的观察者模式
  44. * @return void
  45. * @Author: fengzi
  46. * @Date: 2024/5/21 11:18
  47. */
  48. public function mySubject()
  49. {
  50. // 创建被观察者
  51. $subject = new ConcreteSubject();
  52. // 创建观察者
  53. $obA = new \app\admin\service\mode\observers\my\ConcreteObserverA();
  54. $obB = new \app\admin\service\mode\observers\my\ConcreteObserverB();
  55. // 注册观察者
  56. $subject->attach($obA);
  57. $subject->attach($obB);
  58. // 被观察者执行业务逻辑并通知给观察者
  59. $subject->doSomething();
  60. // 移除观察者
  61. $subject->detach($obB);
  62. // 被观察者执行业务逻辑并通知给观察者
  63. $subject->doSomething();
  64. dd('结束');
  65. }
  66. }

2.6、运行结果展示

文章转载自:疯子丶pony

原文链接:https://www.cnblogs.com/mklblog/p/18201411

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/721220
推荐阅读
相关标签
  

闽ICP备14008679号