设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

1.概述

一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力。当对象们连接在一起时,它们就可以相互提供服务和信息。

通常来说,当某个对象的状态发生改变时,你仍然需要对象之间能互相通信。但是出于各种原因,你也许并不愿意因为代码环境的改变而对代码做大的修改。也许,你只想根据你的具体应用环境而改进通信代码。或者,你只想简单的重新构造通信代码来避免类和类之间的相互依赖与相互从属。

2.问题

当一个对象的状态发生改变时,你如何通知其他对象?是否需要一个动态方案――一个就像允许脚本的执行一样,允许自由连接的方案?

3.解决方案

             观测模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

观测模式允许一个对象关注其他对象的状态,并且,观测模式还为被观测者提供了一种观测结构,或者说是一个主体和一个客体。主体,也就是被观测者,可以用来联系所有的观测它的观测者。客体,也就是观测者,用来接受主体状态的改变 观测就是一个可被观测的类(也就是主题)与一个或多个观测它的类(也就是客体)的协作。不论什么时候,当被观测对象的状态变化时,所有注册过的观测者都会得到通知。
观测模式将被观测者(主体)从观测者(客体)种分离出来。这样,每个观测者都可以根据主体的变化分别采取各自的操作。(观测模式和Publish/Subscribe模式一样,也是一种有效描述对象间相互作用的模式。)观测模式灵活而且功能强大。对于被观测者来说,那些查询哪些类需要自己的状态信息和每次使用那些状态信息的额外资源开销已经不存在了。另外,一个观测者可以在任何合适的时候进行注册和取消注册。你也可以定义多个具体的观测类,以便在实际应用中执行不同的操作。
将一个系统分割成一系列相互协作的类有一个常见的副作用:需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,因为这样降低了它们的可重用性。

4.适用性

在以下任一情况下可以使用观察者模式:
• 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
• 当对一个对象的改变需要同时改变其它对象 , 而不知道具体有多少对象有待改变。
• 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之 , 你不希望这些对象是紧密耦合的。

5.结构

6.模式的组成

观察者模式包含如下角色:
目标(Subject): 目标知道它的观察者。可以有任意多个观察者观察同一个目标。 提供注册和删除观察者对象的接口。
具体目标(ConcreteSubject):  将有关状态存入各ConcreteObserver对象。
观察者(Observer):  为那些在目标发生改变时需获得通知的对象定义一个更新接口。当它的状态发生改变时, 向它的各个观察者发出通知。
具体观察者(ConcreteObserver):   维护一个指向ConcreteSubject对象的引用。存储有关状态,这些状态应与目标的状态保持一致。实现O b s e r v e r的更新接口以使自身状态与目标的状态保持一致。

7.效果

Observer模式允许你独立的改变目标和观察者。你可以单独复用目标对象而无需同时复用其观察者, 反之亦然。它也使你可以在不改动目标和其他的观察者的前提下增加观察者。
下面是观察者模式其它一些优点:
1 )观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
2 )在观察目标和观察者之间建立一个抽象的耦合 :一个目标所知道的仅仅是它有一系列观察者 , 每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。因为目标和观察者不是紧密耦合的, 它们可以属于一个系统中的不同抽象层次。一个处于较低层次的目标对象可与一个处于较高层次的观察者通信并通知它 , 这样就保持了系统层次的完整。如果目标和观察者混在一块 , 那么得到的对象要么横贯两个层次 (违反了层次性), 要么必须放在这两层的某一层中(这可能会损害层次抽象)。
3) 支持广播通信 :不像通常的请求, 目标发送的通知不需指定它的接收者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣 ;它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。
4) 观察者模式符合“开闭原则”的要求。
观察者模式的缺点
1) 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2) 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
4)  意外的更新 因为一个观察者并不知道其它观察者的存在 , 它可能对改变目标的最终代价一无所知。在目标上一个看似无害的的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外 , 如果依赖准则的定义或维护不当,常常会引起错误的更新 , 这种错误通常很难捕捉。
      简单的更新协议不提供具体细节说明目标中什么被改变了 , 这就使得上述问题更加严重。如果没有其他协议帮助观察者发现什么发生了改变,它们可能会被迫尽力减少改变。

8.实现

在php的SPL支持观察者模式,SPL 提供了 SplSubject 和 SplObserver 接口。

SplSubject 接口提供了attach()、detach()、notify() 三个方法。而 SplObserver 接口则提供了 update()方法。

SplSubject 派生类维护了一个状态,当状态发生变化时 - 比如属性变化等,就会调用 notify() 方法,这时,之前在 attach() 方法中注册的所有 SplObserver 实例的 update() 方法就会被调用。接口定义如下:

  1. <?php
  2. /**
  3. * 这一模式的概念是SplSubject类维护了一个特定状态,当这个状态发生变化时,它就会调用notify()方法。
  4. * 调用notify()方法时,所有之前使用attach()方法注册的SplObserver实例的update方法都会被调用。
  5. *
  6. */
  7. interface SplSubject{
  8. public function attach(SplObserver $observer);//注册观察者
  9. public function detach(SplObserver $observer);//释放观察者
  10. public function notify();//通知所有注册的观察者
  11. }
  12. interface SplObserver{
  13. public function update(SplSubject $subject);//观察者进行更新状态
  14. }

实现代码:

  1. <?php
  2. /**
  3. *具体目标
  4. *
  5. */
  6. class ConcreteSubject implements SplSubject {
  7. private $observers, $value;
  8. public function __construct() {
  9. $this->observers = array();
  10. }
  11. public function attach(SplObserver $observer) { //注册观察者
  12. $this->observers[] = $observer;
  13. }
  14. public function detach(SplObserver $observer) { //释放观察者
  15. if($idx = array_search($observer,$this->observers,true)) {
  16. unset($this->observers[$idx]);
  17. }
  18. }
  19. public function notify() { //通知所有观察者
  20. foreach($this->observers as $observer) {
  21. $observer->update($this);
  22. }
  23. }
  24. public function setValue($value) {
  25. $this->value = $value;
  26. $this->notify();
  27. }
  28. public function getValue() {
  29. return $this->value;
  30. }
  31. }
  32. /**
  33. * 具体观察者
  34. *
  35. */
  36. class ConcreteObserver1 implements SplObserver {
  37. public function update(SplSubject $subject) {
  38. echo 'ConcreteObserver1  value is ',$subject->getValue(), '<br>';
  39. }
  40. }
  41. /**
  42. * 具体观察者
  43. *
  44. */
  45. class ConcreteObserver2 implements SplObserver {
  46. public function update(SplSubject $subject) {
  47. echo 'ConcreteObserver2 value is ', $subject->getValue(), '<br>';
  48. }
  49. }
  50. $subject = new ConcreteSubject();
  51. $observer1 = new ConcreteObserver1();
  52. $observer2 = new ConcreteObserver2();
  53. $subject->attach($observer1);
  54. $subject->attach($observer2);
  55. $subject->setValue(5);
  56. ?>

我们扩展上面的例子,根据目标状态而更新不同的观察者:

  1. <?php
  2. /**
  3. *具体目标
  4. *
  5. */
  6. class ConcreteSubject implements SplSubject {
  7. private $observers, $_state;
  8. public function __construct() {
  9. $this->observers = array();
  10. }
  11. /**
  12. *  注册观察者
  13. *
  14. * @param SplObserver $observer
  15. */
  16. public function attach(SplObserver $observer) {
  17. $this->observers[] = $observer;
  18. }
  19. /**
  20. *  //释放观察者
  21. *
  22. * @param SplObserver $observer
  23. */
  24. public function detach(SplObserver $observer) {
  25. if($idx = array_search($observer,$this->observers,true)) {
  26. unset($this->observers[$idx]);
  27. }
  28. }
  29. /**
  30. * 通知所有观察者
  31. *
  32. */
  33. public function notify() {
  34. /**
  35. * 只要状态改变,就通知观察者
  36. */
  37. foreach($this->observers as $observer) {
  38. if ($observer->getState() == $this->_state) {
  39. $observer->update($this);
  40. }
  41. }
  42. }
  43. /**
  44. * 设置状态
  45. *
  46. * @param unknown_type $state
  47. */
  48. public function setState($state) {
  49. $this->_state = $state;
  50. $this->notify();
  51. }
  52. public function getState() {
  53. return $this->_state;
  54. }
  55. }
  56. /**
  57. * 抽象观摩者
  58. *
  59. */
  60. abstract class bserver{
  61. private $_state;
  62. function __construct($state) {
  63. $this->_state = $state;
  64. }
  65. public function setState($state) {
  66. $this->_state = $state;
  67. $this->notify();
  68. }
  69. public function getState() {
  70. return $this->_state;
  71. }
  72. }
  73. /**
  74. * 具体观察者 1
  75. *
  76. */
  77. class ConcreteObserver1 extends bserver  implements SplObserver {
  78. function __construct($state) {
  79. parent::__construct($state);
  80. }
  81. public function update(SplSubject $subject) {
  82. echo 'ConcreteObserver1  state is ',$subject->getState(), '<br>';
  83. }
  84. }
  85. /**
  86. * 具体观察者 2
  87. *
  88. */
  89. class ConcreteObserver2 extends bserver   implements SplObserver {
  90. function __construct($state) {
  91. parent::__construct($state);
  92. }
  93. public function update(SplSubject $subject) {
  94. echo 'ConcreteObserver2 state is ', $subject->getState(), '<br>';
  95. }
  96. }
  97. /**
  98. * 具体观察者 3
  99. *
  100. */
  101. class ConcreteObserver3 extends bserver   implements SplObserver {
  102. function __construct($state) {
  103. parent::__construct($state);
  104. }
  105. public function update(SplSubject $subject) {
  106. echo 'ConcreteObserver3 state is ', $subject->getState(), '<br>';
  107. }
  108. }
  109. $subject = new ConcreteSubject();
  110. $observer1 = new ConcreteObserver1(1);
  111. $observer2 = new ConcreteObserver2(1);
  112. $observer3 = new ConcreteObserver3(2);
  113. $subject->attach($observer1);
  114. $subject->attach($observer2);
  115. $subject->attach($observer3);
  116. echo 'Subject state is 1', '<br>';
  117. $subject->setState(1);
  118. echo 'Subject state is 2', '<br>';
  119. $subject->setState(2);
  120. ?>

9.与其他相关模式

1) 终结者模式Mediator: 通过封装复杂的更新语义 , ChangeManager充当目标和观察者之间的中介者。
2) 单间模式Singleton: ChangeManager可使用Singleton模式来保证它是唯一的并且是可全局访问
的。

10.总结与分析

通过Observer模式,把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。
 

设计模式 ( 十六 ) 观察者模式Observer(对象行为型)的更多相关文章

  1. 设计模式之十:观察者模式(Observer)

    观察者模式: 在对象之间定义了一种一对多的依赖关系.当一个对象改变它的状态时,全部依赖它的对象会自己主动接收通知并更新自己的状态. Define a one-to-many dependency be ...

  2. 设计模式 ( 十六 ): Mediator中介者模式 -- 行为型

    1.概述 在面向对象的软件设计与开发过程中,根据“单一职责原则”,我们应该尽量将对象细化,使其只负责或呈现单一的职责,即将行为分布到各个对象中. 对于一个模块或者系统,可能由很多对象构成,而且这些对象 ...

  3. 我理解设计模式C++实现观察者模式Observer Pattern

    概述: 近期中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑client上,网页上,手机上,iPad上都能够查看到该证券的实时行情,这样的情况下我们应该怎么设 ...

  4. JavaScript 要点(十六)RegExp 对象

    RegExp:是正则表达式(regular expression)的简写. RegExp 对象 正则表达式是描述字符模式的对象. 正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大 ...

  5. PHP设计模式笔记六:数据对象映射模式 -- Rango韩老师 http://www.imooc.com/learn/236

    数据对象映射模式 1.数据对象映射模式,是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作 2.在代码中实现数据对象映射模式,我们将实现一个ORM类,将复杂的SQL语句映射成对象属性 ...

  6. 设计模式--命令模式Command(对象行为型)

    一.命令模式 将一个请求封装为一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. (1)Command类:是一个抽象类,类中对需要执行的命令进行 ...

  7. 【转】设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)

    设计模式 ( 十五 ) 中介者模式Mediator(对象行为型) 1.概述 在面向对象的软件设计与开发过程中,根据"单一职责原则",我们应该尽量将对象细化,使其只负责或呈现单一的职 ...

  8. 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...

  9. 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...

随机推荐

  1. 在ubuntu server中安装和配置docker

    经过一段时间针对不同版本的学习,现在总结当前最新的安装配置过程(应该也是比较简单的) 如果不清楚什么是docker,请参考 https://www.docker.com/ 准备工作 建议在安装之前运行 ...

  2. mybatis入门基础(四)----输入映射和输出映射

    一:输入映射 通过parameterType指定输入参数的类型,类型可以是简单类型.hashmap.pojo的包装类型. 1.1.传递pojo的包装对象 1.1.1.需求描述 完成用户信息的综合查询, ...

  3. 开源代码分析之Android/iOS Hybrid JSBridge框架

    Hybrid开发是现在的主流形式,对于业务快速迭代的公司尤其重要.曾将在鞋厂接触了很多关于Hybrid的理念,在这里分享一些Hybrid框架思想. Hybrid框架包括Native与H5的通信,Web ...

  4. continue用法

    之前,一直没搞懂continue的用法,现在知道其中一个了: continue语句用于终止本轮循环,返回循环头部,开始下一轮循环 var i = 0; while (i < 10){ i++; ...

  5. OpenCV中的矩阵操作

    函数 Description 说明 cvAdd Elementwise addition of two arrays 两个数组对应元素的和 cvAddS Elementwise addition of ...

  6. WCF Misconfiguration: Insufficient Audit Failure Handling

    Abstract: The program is configured not to generate an exception when it fails to write to an audit ...

  7. WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要针对WPF项目 ...

  8. Dijkstra算法(一)之 C语言详解

    本章介绍迪杰斯特拉算法.和以往一样,本文会先对迪杰斯特拉算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法 ...

  9. Gradle常用命令

    使用cmd进入Android studio项目的根目录就可以执行一些gradle相关命令 gradle -v 查看版本 (如果你是第一次执行会去下载Gradle,这个过程如果不FQ非常慢) gradl ...

  10. php对二维数组进行相关操作(排序、转换、去空白等)

    php对二维数组进行相关操作(排序.转换.去空白等) 投稿:lijiao 字体:[增加 减小] 类型:转载 时间:2015-11-04   这篇文章主要介绍了php对二维数组进行相关操作,包括php对 ...