第六章 观察者模式

文章目录

  • 第六章 观察者模式
  • 一、介绍
  • 二、结构
  • 三、实现
  • 四、Observable 类和 Observer 接口

一、介绍

在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其它对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到十字路口时,遇到红灯会停,当绿灯亮起时我们会继续前进

在编程开发的环境中也是如此,比如 MVC 模式中的模型与视图的关系

观察者模式(Observer Pattern)的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,这种模式有时又被称作发布-订阅模式、模型-视图模式

应用场景:

  1. 对象间存在一对多的关系,一个对象的状态发生改变会影响其它对象
  2. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时,可将这二者封装在独立地对象中以使它们可以各自独立地改变和复用
  3. 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播
  4. 多层级嵌套使用,形成一种链式触发机制,使得时间具备跨域(跨越两种观察者类型)通知

观察者模式是一种行为型模式,其主要优点如下:

  1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则
  2. 目标与观察者之技安建立了一套触发机制

它的主要缺点如下:

  1. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
  2. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率

二、结构

观察者模式的主要角色如下:

  1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法
  2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象
  3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用
  4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态

其 UML 类图如下所示:

package com.sisyphus.observer;import java.util.ArrayList;
import java.util.List;/*** @Description: 观察者模式* @Param: $* @return: $* @Author: Sisyphus* @Date: 8/31*/
public class ObserverPattern {public static void main(String[] args) {Subject subject = new ConcreteSubject();Observer obs1 = new ConcreteObserver1();Observer obs2 = new ConcreteObserver2();subject.add(obs1);subject.add(obs2);subject.notigyObserver();}
}//抽象观察者
interface Observer{//反应void response();
}//抽象目标
abstract class Subject{protected List<Observer> observers = new ArrayList<>();//增加观察者方法public void add(Observer observer){observers.add(observer);}//删除观察者方法public void remove(Observer observer){observers.remove(observer);}//通知观察者方法public abstract void notigyObserver();
}//具体目标
class ConcreteSubject extends Subject{@Overridepublic void notigyObserver() {System.out.println("具体目标发生改变...");System.out.println("-------------");for (Object obs : observers){((Observer)obs).response();}}
}//具体观察者1
class ConcreteObserver1 implements Observer{@Overridepublic void response() {System.out.println("具体观察者 1 作出反应");}
}//具体观察者 2
class ConcreteObserver2 implements Observer{@Overridepublic void response() {System.out.println("具体观察者 2 作出反应");}
}

运行结果:

具体目标发生改变...
-------------
具体观察者 1 作出反应
具体观察者 2 作出反应

三、实现

利用观察者模式设计一个程序,分析 ”人民币汇率“ 的升值或贬值对进口公司进口成本或出口公司的出口产品收入以及公司利润率的影响。当 ”人民币汇率“ 升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;当 ”人民币汇率“ 贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升

这里的汇率(Rate)类是抽象目标类,它包含了保存观察者(Company)的 List 和增加/删除观察者的方法,以及有关汇率改变的抽象方法 change(int number);而人民币汇率(RMBrate)类是具体目标,它实现了父类的 change(int number)方法,即当人民币汇率发生改变时通知相关公司;公司(Company)类是抽象观察者,它定义了一个有关汇率反应的抽象方法 response(int number);进口公司(ImportCompany)类和出口公司(ExportCompany)类是具体观察者类,它们实现了父类的 response(int number)方法,即当它们接收到汇率发生改变的通知时作出相应的反应

下图是其 UML 类图

package com.sisyphus.observer;import java.util.ArrayList;
import java.util.List;/*** @Description: 人民币汇率涨跌* @Param: $* @return: $* @Author: Sisyphus* @Date: 8/31*/
public class RMBrateTest {public static void main(String[] args) {Rate rate = new RMBrate();Company importCompany = new ImportCompany();Company exportCompany = new ExportCompany();rate.add(importCompany);rate.add(exportCompany);rate.change(10);rate.change(-5);}
}//抽象观察者:公司
interface Company{void response(int number);
}//抽象目标:汇率
abstract class Rate{protected List<Company> companys = new ArrayList<>();//增加观察者方法public void add(Company company){companys.add(company);}//删除观察者方法public void remove(Company company){companys.remove(company);}public abstract void change(int number);
}//具体目标:人民币汇率
class RMBrate extends Rate{@Overridepublic void change(int number) {for (Company obs : companys){((Company)obs).response(number);}}
}//具体观察者 1:进口公司
class ImportCompany implements Company{@Overridepublic void response(int number) {if (number > 0){System.out.println("人民币汇率升值" + number + "个基点,降低了进口产品成本,提升了进口公司利润率");}else if(number < 0){System.out.println("人民币汇率贬值" + (-number) + "个基点,提升了进口产品成本,降低了进口公司利润率");}}
}//具体观察者 2:出口公司
class ExportCompany implements Company{@Overridepublic void response(int number) {if (number > 0){System.out.println("人民币汇率升值" + number + "个基点,降低了出口产品收入,降低了出口公司的销售利润率");} else if (number < 0){System.out.println("人民币汇率贬值" + (-number) + "个基点,提升了出口产品收入,提升了出口公司的销售利润率");}}
}

运行结果:

人民币汇率升值10个基点,降低了进口产品成本,提升了进口公司利润率
人民币汇率升值10个基点,降低了出口产品收入,降低了出口公司的销售利润率
人民币汇率贬值5个基点,提升了进口产品成本,降低了进口公司利润率
人民币汇率贬值5个基点,提升了出口产品收入,提升了出口公司的销售利润率

四、Observable 类和 Observer 接口

在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例

Observable 类

Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法

  1. void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中
  2. void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update() 方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知
  3. void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者

Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作

利用 Observable 类和 Observer 接口实现原油期货的观察者模式实例

当原油价格上涨时,空方伤心,多方高兴;当油价下跌时,空方高兴,多方伤心。本实例中的抽象目标(Observable)类在 Java 中已经定义,可以直接定义其子类,即原油期货(OilFutures)类,它是具体目标类,该类中定义一个 SetPrice(float price)方法,当原油数据发生变化时调用其父类的 notifyObservers(Object arg) 方法来通知所有观察者;另外,本实例中的抽象观察者接口(Observer)在 Java 中已经定义,只要定义其子类,即具体观察者类(包括多方类 Bull 和空方类 Bear),并实现 update(Observable o, Object arg) 方法即可

package com.sisyphus.observer;import java.util.Observable;
import java.util.Observer;/*** @Description: 原油期货涨跌* @Param: $* @return: $* @Author: Sisyphus* @Date: 8/31*/
public class CrudeOilFutures {public static void main(String[] args) {OilFutures  oil = new OilFutures();Observer bull = new Bull();Observer bear = new Bear();oil.addObserver(bull);oil.addObserver(bear);oil.setPrice(10);oil.setPrice(-5);}
}//具体目标类:原油期货
class OilFutures extends Observable{private float price;public float getPrice(){return this.price;}public void setPrice(float price){super.setChanged();     //设置内部标志位,注明数据发生变化super.notifyObservers(price);//通知观察者价格浮动this.price = price;}
}//具体观察者类:多方
class Bull implements Observer {@Overridepublic void update(Observable o, Object arg) {Float price = ((Float)arg).floatValue();if (price > 0){System.out.println("油价上涨" + price + "元,多方高兴了");} else{System.out.println("油价下跌" + price + "元,多方伤心了");}}
}//具体观察者类:空方
class Bear implements Observer {@Overridepublic void update(Observable o, Object arg) {Float price = ((Float)arg).floatValue();if (price > 0){System.out.println("油价上涨" + price + "元,空方伤心了");} else{System.out.println("油价下跌" + price + "元,空房高兴了");}}
}

运行结果:

油价上涨10.0元,空方伤心了
油价上涨10.0元,多方高兴了
油价下跌-5.0元,空房高兴了
油价下跌-5.0元,多方伤心了

【设计模式】第六章 观察者模式相关推荐

  1. java设计模式(六)--观察者模式

    转载:设计模式(中文-文字版) 目录: 简单目标任务实现 观察者模式介绍 观察者模式代码实现 观察者模式是JDK中使用最多的模式之一,非常有用.我们也会一并介绍一对多关系,以及松耦合(对,没错,我们说 ...

  2. 第六章 Caché 设计模式 原型模式

    文章目录 第六章 Caché 设计模式 原型模式 定义 使用场景 优点 结构图 描述 示例 初级写法 缺点 中级写法 缺点 高级写法 (浅复制) 浅复制 深复制 完整示例 简历类(复制类) 对象类(工 ...

  3. 【设计模式2022】第六章 建造者模式

    [设计模式2022]第六章 建造者模式 文章目录 [设计模式2022]第六章 建造者模式 一.建造者模式 1.结构 2.案例 3.分析 4.使用场景 5.扩展 6.对比工厂模式 一.建造者模式 将一个 ...

  4. 软件工程学习笔记——第六章 软件设计方法

    目录 第一章 概述 第二章 过程和活动 第三章 软件过程模型 第四章 问题定义和可行性研究方法 第五章 需求分析方法-1 第五章 需求分析方法-2 第六章 软件设计方法 第七章 软件实施与测试方法 第 ...

  5. Java基础学习——第六章 面向对象编程(下)

    Java基础学习--第六章 面向对象编程(下) 一.关键词:static 1. static关键字的引入 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new ...

  6. 走向ASP.NET架构设计-第六章-服务层设计(中篇)

    走向ASP.NET架构设计-第六章-服务层设计(中篇) 前言:上一篇文章介绍了一些服务层的基本知识,而且也简要的介绍了SOA的有关知识,本篇主要是介绍在服务层可以采用的一些模式.  本篇议题如下: F ...

  7. python归一化 增大差异_简学Python第六章__class面向对象编程与异常处理

    Python第六章__class面向对象编程与异常处理 欢迎加入Linux_Python学习群 群号:478616847 目录: 面向对象的程序设计 类和对象 封装 继承与派生 多态与多态性 特性pr ...

  8. 第六章 面向对象(下)

    除前一章所介绍的关于类.对象的基本语法之外,本章将会继续介绍Java面向对象的特性.Java为8个基本类型提供了对应的包装类,通过这些包装类可以把8个基本类型的值包装成对象使用,JDK1.5提供了自动 ...

  9. 云计算设计模式(六)——命令和查询职责分离(CQRS)模式

    云计算设计模式(六)--命令和查询职责分离(CQRS)模式 隔离,通过使用不同的接口,从操作读取数据更新数据的操作.这种模式可以最大限度地提高性能,可扩展性和安全性;支持系统在通过较高的灵活性,时间的 ...

最新文章

  1. 提升思辨能力和判断力
  2. 什么镜头最适合拍风景_尼康Z口20mm的镜头拍什么好
  3. Kafka删除topic Note: This will have no impact if delete.topic.enable is not【另外强烈推荐一个kafka小工具】
  4. 网易云音乐评论催泪刷屏?我用Python抓取了1008328条热评告诉你为什么!
  5. 基于JAVA+SpringMVC+Mybatis+MYSQL的汽车租赁系统
  6. mac系统不能使用127.0.0.2的解决方案
  7. html三级下拉栏插件,纯js超酷下拉框插件tastySelect
  8. 数字人轻松学习Blender系列之八:建模-1
  9. 太极图php代码,如何实现太极图
  10. 辛苦整理的 C/C++ 笔记,请惠存!
  11. 吐个槽:bose的售后真心差劲!愧对这个顶级音响产品!
  12. 46泰勒中值定理的常规证明
  13. 一般来说仿制一个网站大概需要多少钱呢
  14. 2020年,技术圈十大“翻车”事件!
  15. DAVIS: Densely Annotated VIdeo Segmentation
  16. 【WSN通信】能量均衡的无线传感器网络非均匀分簇路由协议附matlab代码
  17. c语言程序设计基础程序改错,c语言程序设计改错信息.docx
  18. ol+天地图+geoserver_教程:使用GeoServer发布离线地图服务(WMS)
  19. 世平信息成功通过CMMI 3级认定
  20. SMT集成电路板MES系统解决方案

热门文章

  1. 抓取经过无线路由器的数据_无线网关是什么 无线网关的作用
  2. 计算机设备 运维,计算机网络设备运维报告
  3. 水文特点是什么意思_自动气象站应建在什么地方?
  4. 【转】Java计算文件的hash值
  5. 查询表达式和LINQ to Objects
  6. js设置时间在ie中部兼容总是NAN的问题
  7. aspnet拒绝ip访问_代理ip的好处是什么?
  8. 广州的11个辖区_重庆前三季度GDP反超广州,这对两城到底意味着什么?
  9. MySQL深度剖析之SQL语句更新流程(2021)
  10. (60)UART外设驱动发送驱动(五)(第12天)