一、概述
Observer(观察者)模式又被称作发布-订阅(Publish -Subscribe)模式,用于定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

二、结构
Observer模式的结构如下图所示:

图 1、Observer模式类图示意
上面的类图中包括如下组成部分:
Subject(抽象主题)角色:主题角色把所有对观察考对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。 
Observer(抽象观察者)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update ()方法),这个方法叫做更新方法。 
ConcreteSubject(具体主题)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。 
ConcreteObserver(具体观察者)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

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

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

五、举例
Observer模式是一个应用比较广泛的模式,在Java中,Observer模式的应用随处可见(可参考笔记 13中关于Java中CoR与Observer模式的对比部分内容),此外,JDK中特别提供了java .util .Observable类和Observer接口来方便程序员应用Observer模式,而C ++方面,ATL则提供了IConnectionPointContainer、IConnectionPoint、IEnumConnectionPoints、IEnumConnections等以支持所谓的连接点及可连接对象等(相关示例见参考 1),而在MFC中,一个文档可以对应多个视图,而当文档发生更新后,可以通过UpdateAllViews来同步更新所有视图,这虽然并非严格意义上的Observer模式的应用,但其本质是相似的。
前面曾经从Mediator模式的角度实现过一个ChatRoom的例子,这里用Observer模式来解决同一问题:

#include <vector>
#include <iostream>
using namespace std ;

class ChatRoom ;
struct Observer
{

    virtual  void SetChatRoom (ChatRoom * pChatRoom ) =  0 ;
    virtual  void Notify ( const string & from ,  const string & to ,  const string & msg ) =  0 ;
};

class ChatRoom     // Subject
{
private :
    vector <Observer *> vo ;
public :
    virtual  ~ChatRoom ()
    {

        vector <Observer *>::iterator vi  = vo .begin ();
        for  (; vi  != vo .end (); vi ++)
        {

            delete  *vi ;
        }
    }

    void Login (Observer * po )
    {

        vo .push_back (po );
        po ->SetChatRoom ( this );
    }

    void Logout (Observer * po )
    {

        vector <Observer *>::iterator vi  = vo .begin ();
        for  (; vi  != vo .end (); vi ++)
        {

            if  (*vi  == po )
                vo .erase (vi );
        }
    }

    void Notify ( const string & from ,  const string & to ,  const string & msg )
    {

        vector <Observer *>::iterator vi  = vo .begin ();
        for  (; vi  != vo .end (); vi ++)
        {
            (*
vi )->Notify (from , to , msg );
        }
    }
};

class Chater  :  public Observer     // ConcreteObserver
{
private :
    string name ;
    ChatRoom * pChatRoom ;
public :
    Chater ( const string & name ) : name (name ) {}
    void SetChatRoom (ChatRoom * pChatRoom )
    {

        this ->pChatRoom  = pChatRoom ;
    }

    void Send ( const string & to ,  const string & msg )
    {

        pChatRoom ->Notify (name , to , msg );
    }

    void Notify ( const string & from ,  const string & to ,  const string & msg )
    {

        if  ( 0  == to .compare (name ))
        {

            cout  <<  "["  << from .c_str () <<  "] to ["  << to .c_str () <<  "]: "
                <<
 msg .c_str () << endl ;
        }
    }
};

int  main ()
{

    ChatRoom cr ;

Chater * c1  =  new Chater ( "David" );
    Chater * c2  =  new Chater ( "Jordan" );
    Chater * c3  =  new Chater ( "O'Neal" );

cr .Login (c1 );
    cr .Login (c2 );
    cr .Login (c3 );

c1 ->Send ( "Jordan" ,  "You are a great basketball player" );     // Send message to change subject state
    c2 ->Send ( "O'Neal" ,  "Work hard, you are great!" );

return  0 ;
}

在上述示例中,引起Subject(即ChatRoom)发生变化的是Observer(即Chater),而接收Subject发出的Notify消息的同样是Observer,这在有些应用中可能是两个 /类不同的实体。此外,在上述示例中,当ChatRoom接收到消息时,不会对消息的内容进行解析,而是直接通过Notify通知所有Chater,由所有Chater自己负责检查该消息是否是发送给自己的(实际的ChatRoom不可能采用这种形式),这在一定程度上与广播非常相似,所以有时候,我们可以采用Observer模式来模拟软件广播。
以上示例中,Subject不对消息的内容进行检查,而是盲目地进行广播,在具体应用中,我们可以结合使用Mediator模式和Observer模式,将Subject设计成一个Mediator,对消息内容进行检查,进而根据消息内容进行消息的Publish。

参考:
1、http : //www.codeproject.com/com/connectionpoint.asp

大卫的Design Patterns学习笔记19:Observer相关推荐

  1. Ext.Net学习笔记19:Ext.Net FormPanel 简单用法

    Ext.Net学习笔记19:Ext.Net FormPanel 简单用法 FormPanel是一个常用的控件,Ext.Net中的FormPanel控件同样具有非常丰富的功能,在接下来的笔记中我们将一起 ...

  2. springmvc学习笔记(19)-RESTful支持

    springmvc学习笔记(19)-RESTful支持 标签: springmvc springmvc学习笔记19-RESTful支持 概念 REST的样例 controller REST方法的前端控 ...

  3. 设计模式学习笔记——观察者(Observer)模式

    设计模式学习笔记--观察者(Observer)模式 @(设计模式)[设计模式, 观察者模式, Observer] 设计模式学习笔记观察者Observer模式 基本介绍 观察者案例 类图 实现代码 Ob ...

  4. 区块链学习笔记19——ETH难度调整

    区块链学习笔记19--ETH难度调整 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 前面学过,比特 ...

  5. Python学习笔记19:列表 III

    Python学习笔记19:列表 III 其实这篇笔记标题应该是列表扩展,从列表开始,将涵盖Python中的序列容器. 关于列表的基础知识,可以看我的前两篇文章: Python学习笔记1:列表. Pyt ...

  6. Linux 学习笔记19 信号

    Linux 学习笔记19 信号 信号 信号概述 为什么要是使用信号--为了实现进程的有序退出 信号是进程运行过程中,由自身产生或者由进程外部发来的消息.信号是硬件中断的软件模拟(软中断) signal ...

  7. Synopsys Design compiler 学习笔记(收藏)

    IC学习・成长加油站 LoveIC Synopsys Design compiler 学习笔记    design compiler流程 Design compiler工作流程大致分为四步: 1)Lo ...

  8. 影像组学视频学习笔记(19)-数据标准化、归一化极简概述、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(19)主要介绍: 数据的标准化.归一化 为什么要进行标准化.归一化? 机器学习算法的要求 便于横向比较 # 标准化 (影像组学中最常用) ...

  9. TMS320F280049C 学习笔记19 可配置逻辑块 (CLB) 软件配置

    文章目录 CLB tool 简介 CLB配置过程概述 软件安装 GNU Compiler Install the Simulation Viewer 使用CLB tool 导入空CLB工程 更新变量路 ...

最新文章

  1. 袁崇焕·任志强·张纪中
  2. 1Python全栈之路系列Web框架介绍
  3. 蓝牙Bluetooth技术手册规范下载【转】
  4. java8升级java12_为什么现在是升级到Java 8的最佳时机
  5. Struts项目中,检测用户名是否被占用/查询账户名称是否被占用/查询账户名称是否已被注册/检查用户名是否被注册
  6. 实例2:python
  7. python下载图片的命令_网上的图片不知道怎么批量下载?python教你怎么把网站上面的图片都爬下来...
  8. python输出不重复的单词_Python情感分析(比较单词时,不计算文本中重复的单词)...
  9. Percona XtraDB cluster--第一部分:XtraDB cluster安装 (Centos7)
  10. scala基础之控制结构
  11. 文件下的所有文本内容转为一个csv文件代码
  12. Effective_STL 学习笔记(三) 使容器里对象的拷贝操作轻量而正确
  13. php-php异步网络通信引擎-服务发现-消息队列 案例
  14. 免费WiFi上网软件是什么?怎么用?
  15. 轻雀协作客户最佳实践之凯叔讲故事
  16. 计算机开机进不去桌面,电脑开机进不了桌面,怎么破?
  17. xms java_java xms xmx xmn xss解释
  18. 网络布线之有线传输+UTP线缆连接
  19. grep 与正则表达式
  20. MariaDB 分区

热门文章

  1. java 保留字符串中的数字_java从字符串中提取数字
  2. WEB 安全性测试用例
  3. 虚拟创业云|BBC幼儿英语启蒙动画Feeling Better让孩子学会做情绪的主人全25集
  4. 2020-08-19
  5. Jenkins job A触发job B的方法和踩坑指南
  6. 【微信公众号开发】六、微信JS的使用
  7. 等待事件系列(1)--User I/O类型
  8. pin function和pin group:iomuxc节点解析始末
  9. 免费sip软电话软件(sip客户端)
  10. 你不知道的哪些办公软件?