设计模式——观察者模式
发布日期:2021-06-29 20:01:47 浏览次数:2 分类:技术文章

本文共 7821 字,大约阅读时间需要 26 分钟。

观察者模式

这篇博文是根据B站尚硅谷设计模式课程韩老师所讲而做的,在此表示感谢!

文章目录

观察者模式:对象之间多对一依赖的一种设计方案,
被依赖的对象为 Subject
依赖的对象为 Observer
Subject通知 Observer 变化,比如奶站\气象局\报社等就类似于 Subject,Subject是 1 的一方,Observer是用户是多的一方;

基本介绍:

  • 观察者模式类似 订奶\订报\天气平台等业务
  • Subject:主要是登记注册、移除和通知Observer(观察者),主要有如下方法:
    • registerObserver() 注册观察者
    • removeObserver() 移除观察者
    • notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送, 看具体需求定;
  • Observer:观察者,主要接收Subject的通知;

案例介绍

我们通过如下一个案例来演示观察者模式,问题如下:

  • 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方);
  • 需要设计开放型 API,便于其他第三方也能接入气象站获取数据;
  • 提供温度、气压和湿度的接口;
  • 测量数据更新时,要能实时的通知给第三方;

UML

在这里插入图片描述

编码

编写观察者的接口类

观察者的接口类Observer如下所示:

package edu.hebeu.observer.observer;/** * 观察者接口 * @author 13651 * */public interface Observer {
void update(float temperature, float pressure, float humidity);}

编写管理观察者的接口

管理观察者的接口Subject,包括了对观察者的注册,删除,通知等方法,如下

package edu.hebeu.observer.subject;import edu.hebeu.observer.observer.Observer;/** *  * @author 13651 * */public interface Subject {
/** * 注册观察者 * @param observer */ void registerObserver(String observerName, Observer observer); /** * 删除观察者 * @param observer */ void removeObserver(String observerName); /** * 通知所有的观察者 */ void notifyObservers();}

编写能够测量、修改天气数据的类

该类嗨需要实现Subect接口,使该类能够获得其内部的特性,如,注册、删除、通知观察者,以保证在每次数据更新时数据能实时的发送至观察者,让观察者将最新的数据展示,代码如下:

package edu.hebeu.observer.subject;import java.util.HashMap;import java.util.Map;import java.util.Set;import edu.hebeu.observer.observer.Observer;public class WeatherData implements Subject{
// 温度,气压,湿度 private float temperatrue; private float pressure; private float humidity; /** * 观察者集合 */ private Map
observers; public WeatherData() {
observers = new HashMap<>(); } /** * 获取温度 * @return */ public float getTemperature() {
return temperatrue; } /** * 获取气压 * @return */ public float getPressure() {
return pressure; } /** * 获取湿度 * @return */ public float getHumidity() {
return humidity; } /** * 当数据有更新时,调用的函数 * @param temperature * @param pressure * @param humidity */ public void setData(float temperature, float pressure, float humidity) {
this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; //调用 notifyObservers, 将最新的信息 推送给 接入方 所有的观察者 notifyObservers(); } @Override public void registerObserver(String observerName, Observer observer) {
observers.put(observerName, observer); System.out.println("添加《" + observerName + "》观察者成功!"); } @Override public void removeObserver(String observerName) {
if(observers.containsKey(observerName)) {
observers.remove(observerName); System.out.println("删除《" + observerName + "》观察者成功!"); return; } System.err.println("未找到《" + observerName + "》观察者,删除失败!"); } @Override public void notifyObservers() {
Set
> observerSet = observers.entrySet(); for(Map.Entry
observer : observerSet) {
observer.getValue().update(this.temperatrue, this.pressure, this.humidity); } }}

编写"第三方的接口“

实际上就类似于MySQL、Oracle实现Java提供的数据库驱动一样,我们为了模拟需要提供如下的几个类:

BaiDu

package edu.hebeu.observer.observer;public class BaiDu implements Observer{
// 温度,气压,湿度 private float temperature; private float pressure; private float humidity; /** * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式 * @param temperature * @param pressure * @param humidity */ @Override public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * 显示数据 */ public void display() {
System.out.println("========================百度天气========================="); System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***"); }}

WangYi

package edu.hebeu.observer.observer;public class WangYi implements Observer{
// 温度,气压,湿度 private float temperature; private float pressure; private float humidity; /** * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式 * @param temperature * @param pressure * @param humidity */ @Override public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * 显示数据 */ public void display() {
System.out.println("========================网易天气========================="); System.out.println("&&&Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "---"); System.out.println("^^^Today mHumidity: " + humidity + "***"); }}

XinLang

package edu.hebeu.observer.observer;public class XinLang implements Observer{
// 温度,气压,湿度 private float temperature; private float pressure; private float humidity; /** * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式 * @param temperature * @param pressure * @param humidity */ @Override public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * 显示数据 */ public void display() {
System.out.println("========================新浪天气========================="); System.out.println("---Today mTemperature: " + temperature + "---"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("+++Today mHumidity: " + humidity + "+++"); }}

可以发现我们需要什么订阅者,直接就实现Observer接口就好了,非常方便;

编写测试类

编写上述代码的测试类Client,如下所示:

package edu.hebeu.observer;import java.util.Scanner;import edu.hebeu.observer.observer.BaiDu;import edu.hebeu.observer.observer.WangYi;import edu.hebeu.observer.observer.XinLang;import edu.hebeu.observer.subject.WeatherData;public class Client {
private static float TEMPERATURE, PRESSURE, HUMIDITY; // 声明保持温度、湿度、气压的静态变量 public static void main(String[] args) {
WeatherData weatherData = new WeatherData(); // 注册初始的观察者 weatherData.registerObserver("百度天气", new BaiDu()); weatherData.registerObserver("新浪天气", new XinLang()); weatherData.registerObserver("网易天气", new WangYi()); // 更新信息 weatherData.setData(37f, 158.9f, 29f); Scanner scanner = new Scanner(System.in); while(true) {
System.out.println();System.out.println();System.out.println(); System.out.println("\"u(update)\"更新数据"); System.out.println("\"a(add)\"添加观察者"); System.out.println("\"r(remove)\"删除观察者"); System.out.println("\"e(exit)\"退出程序"); System.out.print("请输入:");Character keyword = scanner.next().charAt(0); if(keyword.equals('u')) {
System.out.print("温度:"); TEMPERATURE = scanner.nextFloat(); System.out.print("湿度:"); PRESSURE = scanner.nextFloat(); System.out.print("气压:"); HUMIDITY = scanner.nextFloat(); System.out.println("--------------------------------------------天气更新-------------------------------"); weatherData.setData(TEMPERATURE, PRESSURE, HUMIDITY); // 改变数据 } else if(keyword.equals('a')) {
System.out.print("请输入添加的观察者名:"); String observerName = scanner.next(); if(observerName.equals("百度天气")) {
weatherData.registerObserver(observerName, new BaiDu()); } else if(observerName.equals("新浪天气")) {
weatherData.registerObserver(observerName, new XinLang()); } else if(observerName.equals("网易天气")) {
weatherData.registerObserver(observerName, new WangYi()); } else {
System.err.println("第三方库中未找到" + observerName + "接口,添加失败!"); } } else if(keyword.equals('r')) {
System.out.print("请输入删除的观察者名:"); String observerName = scanner.next(); weatherData.removeObserver(observerName); } else if(keyword.equals('e')) {
// 如果输入是 "exit" break; // 退出循环 } } if(scanner != null) {
scanner.close(); } System.out.println("bye~~"); }}

测试

测试更新天气数据,就会自动的通知其他的观察者改变它们的数据,然后将最新的数据显示出来,如下所示:

在这里插入图片描述
也可以将某些注册的观察者删除,如下:
在这里插入图片描述
也可以添加观察者,但是要保证有”该观察者“,如下:
在这里插入图片描述

转载地址:https://blog.csdn.net/m0_49039508/article/details/116732205 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:单向环形链表——约瑟夫问题
下一篇:JUC学习笔记

发表评论

最新留言

很好
[***.229.124.182]2024年04月12日 17时28分00秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章