一、定义

观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

二、结构

一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。

观察者模式结构中通常包括观察目标和观察者两个继承层次结构,其结构如图所示:

观察者模式所涉及的角色有:

  • 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
  • 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
  • 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

三、示例代码

参考GitHub中的事例 https://github.com/qiaojiuyuan/ObserverPattern/tree/master/src ,给出观察者模式类图如下所示:

         抽象主题角色类

  1. public interface Observable {
  2. /**
  3. * 增加一个观察者
  4. * @param observable
  5. */
  6. public void addObserver(Observer observable);
  7. /**
  8. * 删除一个观察者
  9. * @param observable
  10. */
  11. public void deleteObserver(Observer observable);
  12. /**
  13. * 通知观察者
  14. * @param context
  15. */
  16. public void notifyObserviers(String context);
  17. }

被观察者活动

  1. public interface IHanFeiZi {
  2. /**
  3. * 韩非子吃饭
  4. */
  5. public void haveBreakfast();
  6. /**
  7. * 韩非子娱乐
  8. */
  9. public void haveFun();
  10. }

具体主题角色类 

  1. public class HanFeiZi implements Observable,IHanFeiZi{
  2. private ArrayList<Observer> observableList = new ArrayList<>();
  3.  
  4. @Override
  5. public void addObserver(Observer observable) {
  6. this.observableList.add(observable);
  7. }
  8.  
  9. @Override
  10. public void deleteObserver(Observer observable) {
  11. this.observableList.remove(observable);
  12. }
  13.  
  14. @Override
  15. public void notifyObserviers(String context) {
  16. for(Observer observer : observableList) {
  17. observer.update(context);
  18. }
  19. }
  20.  
  21. @Override
  22. public void haveBreakfast() {
  23. System.out.println("韩非子:开始吃饭");
  24. this.notifyObserviers("韩非子在吃饭");
  25. }
  26.  
  27. @Override
  28. public void haveFun() {
  29. System.out.println("韩非子:开始娱乐");
  30. this.notifyObserviers("韩非子在娱乐");
  31. }
  32. }

抽象观察者角色类

  1. public interface Observer {
  2. public void update(String context);
  3. }

具体观察者角色类

  1. public class LiSi implements Observer{
  2. @Override
  3. public void update(String context) {
  4. System.out.println("李斯:观察到韩非子活动,开始向老板汇报");
  5. this.reportToQinShihuang(context);
  6. }
  7.  
  8. private void reportToQinShihuang(String reportcontent) {
  9. System.out.println("报告,韩非子有活动:"+reportcontent);
  10. }
  11. }
  1. public class WangSi implements Observer{
  2. private final static String TAG = WangSi.class.getSimpleName();
  3.  
  4. @Override
  5. public void update(String context) {
  6. // TODO Auto-generated method stub
  7. System.out.println(TAG+"观察到韩非子有变化");
  8. report(context);
  9. }
  10.  
  11. private void report(String context) {
  12. System.out.println("报告韩非子的状态:"+context);
  13. }
  14. }

客户端类

  1. public class ClientMain {
  2. public static void main(String[] args) {
  3. Observer lisi = new LiSi();
  4. Observer wangsi = new WangSi();
  5. HanFeiZi hanFeiZi = new HanFeiZi();
  6. hanFeiZi.addObserver(lisi);
  7. hanFeiZi.addObserver(wangsi);
  8. hanFeiZi.haveBreakfast();
  9. hanFeiZi.haveFun();
  10. }
  11. }

四、特性

1.优点

(1) 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色;
(2) 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次;
(3) 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度;
(4) 观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

2.缺点

(1) 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间;
(2) 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃;
(3) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

3.适用场景

(1) 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用; 
(2) 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁;
(3) 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

五、参考博客

https://www.cnblogs.com/renhui/p/6479748.html

https://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html

观察者模式——Java实例的更多相关文章

  1. Thrift入门及Java实例演示<转载备用>

    Thrift入门及Java实例演示 作者: Michael 日期: 年 月 日 •概述 •下载配置 •基本概念 .数据类型 .服务端编码基本步骤 .客户端编码基本步骤 .数据传输协议 •实例演示(ja ...

  2. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

  3. JAVA实例

     JAVA实例1  1 package Demo3; import java.io.File; import java.io.FileReader; import java.io.IOExceptio ...

  4. Java 实例 - 如何执行指定class文件目录(classpath) Java 实例 J

    Java 实例 - 如何执行指定class文件目录(classpath)  Java 实例 如果我们 Java 编译后的class文件不在当前目录,我们可以使用 -classpath 来指定class ...

  5. Java-Runoob-高级教程-实例-方法:15. Java 实例 – 重载(overloading)方法中使用 Varargs

    ylbtech-Java-Runoob-高级教程-实例-方法:15. Java 实例 – 重载(overloading)方法中使用 Varargs 1.返回顶部 1. Java 实例 - 重载(ove ...

  6. Java-Runoob-高级教程-实例-方法:14. Java 实例 – Varargs 可变参数使用

    ylbtech-Java-Runoob-高级教程-实例-方法:14. Java 实例 – Varargs 可变参数使用 1.返回顶部 1. Java 实例 - Varargs 可变参数使用  Java ...

  7. Java-Runoob-高级教程-实例-方法:13. Java 实例 – for 和 foreach循环使用

    ylbtech-Java-Runoob-高级教程-实例-方法:13. Java 实例 – for 和 foreach循环使用 1.返回顶部 1. Java 实例 - for 和 foreach循环使用 ...

  8. Java-Runoob-高级教程-实例-方法:12. Java 实例 – Enum(枚举)构造函数及方法的使用-um

    ylbtech-Java-Runoob-高级教程-实例-方法:12. Java 实例 – Enum(枚举)构造函数及方法的使用 1.返回顶部 1. Java 实例 - Enum(枚举)构造函数及方法的 ...

  9. Java-Runoob-高级教程-实例-方法:11. Java 实例 – enum 和 switch 语句使用

    ylbtech-Java-Runoob-高级教程-实例-方法:11. Java 实例 – enum 和 switch 语句使用 1.返回顶部 1. Java 实例 - enum 和 switch 语句 ...

随机推荐

  1. linux内核netfilter连接跟踪的hash算法

    linux内核netfilter连接跟踪的hash算法 linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现.conntrack是netfilte ...

  2. 【CSS3基础-Flex布局】

    关于Flex 背景 在flex布局出现以前,常用的水平和垂直居中对齐方式有很多.flex布局的出现基本规范了这一过程. 通过justify-content和align-items两个属性即解决了水平居 ...

  3. 彻底弄懂JS原型与继承

    本文由浅到深,循序渐进的将原型与继承的抽象概念形象化,且每个知识点都搭配相应的例子,尽可能的将其通俗化,而且本文最大的优点就是:长(为了更详细嘛). 一.原型 首先,我们先说说原型,但说到原型就得从函 ...

  4. [iOS]app的生命周期

    对于iOS应用程序,关键的是要知道你的应用程序是否正在前台或后台运行.由于系统资源在iOS设备上较为有限,一个应用程序必须在后台与前台有不同的行为.操作系统也会限制你的应用程序在后台的运行,以提高电池 ...

  5. css 中的 initial inherit unset 意思

    写css时,在对属性进行选值,经常遇到unset , initial,inherit三个值.这几个值的含义. 1.inherit 可继承性 继承的意思. 每一个 CSS 属性都有一个特性就是,这个属性 ...

  6. 【Linux】Linux中VIM编辑器的使用

    vim编辑器是我们在Linux中不可或缺的一部分,我们通常会使用它去自定义编辑文本已达到我们的需求,那么vim文本编辑器具体要怎么使用呢.今天跟大家分享一下vim编辑器的使用: vim编辑器模式介绍 ...

  7. Delphi东京版FireDAC连接MSSQL2000提示对象名 'SYS.DATABASES' 无效

    在Delphi 10.2.1 东京 版中,FireDAC默认不兼容MSSQL2000,会提示“[FireDAC][Phys][ODBC][Microsoft][ODBC SQL Server Driv ...

  8. SELinux初学者指南

    SELinux(Security Enhanced Linux)是美国国家安全局2000年发布的一种高级MAC(Mandatory Access Control,强制访问控制)机制,用来预防恶意入侵. ...

  9. 05ICMP协议与ARP协议(IP协议中重要协议)

    ICMP协议在网络层,应用:ping命令,tracert命令:追踪命令,用于静态路由. ICMP重定向 redirect

  10. 20155203 实验三《敏捷开发与XP实践》实验报告

    20155203 实验三<敏捷开发与XP实践>实验报告 一.实验内容 在IDEA中使用工具(Code->Reformate Code)把下面代码重新格式化,再研究一下Code菜单,找 ...