当前位置: 首页 > news >正文

设计模式之观察者模式详解(Observer Pattern)

模式的定义与特点

定义

观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

概念很清晰,我们举个例子来理解一下观察者模式的含义,我们都在新浪微博中关注过某一位明星(假设,当然很多人已经不玩微博了),每当这位明星发布一条动态时候,他的粉丝就都会知道。我们使用一张图来表示一下他们的关系。
在这里插入图片描述
上面这位明星在新浪微博上发了一条动态,说他会唱、跳rap等等。然后他的粉丝就都知道了。从这个例子中我们可以看到,这里包含了两种人,第一种是明星,第二个是粉丝。转化为设计模式中的语言就是主题和观察者。

我们的明星的微博就相当于与一个主题,粉丝就是观察者,随时观察明星的动态。不过明星有权利让你关注,也有权利把你拉黑。

特点

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

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 目标与观察者之间建立了一套触发机制。

它的主要缺点如下。

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

原理类图与角色说明

现在我们从类图的角度来看一下:
观察者模式的原理类图

角色说明

  1. Subject:抽象主题,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。意思就是明星把所有的粉丝都保存在一个账号里面,粉丝数量不限,可以新增粉丝也可以拉黑粉丝。

  2. ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。意思是我们的明星一有动态,就会把消息给粉丝。

  3. Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。这就是我们所有粉丝的抽象。

  4. ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。具体每一个粉丝。

模式的应用场景

通过前面的分析与应用实例可知观察者模式适合以下几种情形。

  1. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  2. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

应用实例

下面以天气状况推送为例子运用一下观察者模式:

定义抽象观察者(Observer):抽象的天气
//观察者接口,由具体观察者来实现
public interface Observer {
    public void update(float temperature,float pressure,float humidity);
}
具体的观察者(concreteObserver):具体的天气状况使用者
//百度网站类,使用了推送来的天气信息
public class BaiduSite implements Observer {
    // 温度,气压,湿度
    private float temperature;
    private float pressure;
    private float humidity;

    //  更新  天气情况,是由  WeatherData  来调用,这里使用推送模式
    @Override
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }

    //显示
    private void display() {
        System.out.println("===百度网站====");
        System.out.println("***百度网站 气温 : " + temperature + "***");
        System.out.println("***百度网站 气压: " + pressure + "***");
        System.out.println("***百度网站 湿度: " + humidity + "***");
    }
}
//当前天气类,使用了推送来的天气信息
public class CurrentConditions implements Observer {
    // 温度,气压,湿度
    private float temperature;
    private float pressure;
    private float humidity;

    //  更新  天气情况,是由  WeatherData  来调用,这里使用推送模式
    @Override
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }

    //显示
    private void display() {
        System.out.println("===当前天气====");
        System.out.println("***当前天气 气温 : " + temperature + "***");
        System.out.println("***当前天气 气压: " + pressure + "***");
        System.out.println("***当前天气 湿度: " + humidity + "***");
    }
}
定义抽象主题(Subject):抽象天气信息站
//接口, 让 WeatherData 来实现
public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}
具体主题(ConcreteSubject):具体的天气信息站
/**
 * 	此类是核心
 * 	1. 包含最新的天气情况信息
 * 	2. 含有 观察者集合,使用 ArrayList 管理
 * 	3.  当数据有更新时,就主动的调用	ArrayList, 通知所有的(接入方)就看到最新的信息
 */
public class WeatherData implements Subject {
    // 温度,气压,湿度
    private float temperature;
    private float pressure;
    private float humidity;
    List<Observer> observers;

    public WeatherData() {
        observers = new ArrayList<>();
    }

    public void setData(float temperature,float pressure,float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        //调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
        dataChange();
    }

    private void dataChange() {
        //调用 接入方的 update
        notifyObservers();
    }


    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        if (observers.contains(observer)) {
            observers.remove(observer);
        }
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this.temperature,this.pressure,this.humidity);
        }
    }
}
客户端调用测试
public class Client {
    public static void main(String[] args) {
        //创建一个 WeatherData
        WeatherData weatherData = new WeatherData();
        //创建观察者
        BaiduSite baiduSite = new BaiduSite();
        CurrentConditions currentCondition = new CurrentConditions();
        // 注 册 到 weatherData
        weatherData.registerObserver(baiduSite);
        weatherData.registerObserver(currentCondition);
        // 测 试
        System.out.println("通知各个注册的观察者, 看看信息");
        weatherData.setData(25f,88f,47f);

        //移出当前天气的观察者,再次测试
        weatherData.removeObserver(currentCondition);
        System.out.println();
        System.out.println("通知各个注册的观察者, 看看信息");
        weatherData.setData(10f, 100f, 30.3f);
    }
}

参考

23种设计模式之观察者模式
观察者模式(Observer模式)详解

相关文章:

  • 设计模式之迭代器模式详解(Iterator Pattern)
  • asp.net部分优化
  • 设计模式之中介者模式详解(Mediator Pattern)
  • ASP.NET中利用存储过程实现模糊查询
  • 设计模式之备忘录模式详解(Memento Pattern)
  • 从asp转asp.net的相关
  • 设计模式之状态模式详解(State Pattern)
  • 浅谈DataSet 的用法
  • 多维数组和矩阵之顺时针打印二维数组
  • 各种数据库连接代码(JSP)
  • 多维数组与矩阵之0所在的行列清零
  • 多维数组与矩阵之之字形打印矩阵
  • 生姜有味的调色板
  • 设计模式之策略者模式详解(Strategy Pattern)
  • 利用J2ME里的RMS对记录进行排序
  • 「面试题」如何实现一个圣杯布局?
  • Date型的使用
  • es的写入过程
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Mithril.js 入门介绍
  • python学习笔记 - ThreadLocal
  • Xmanager 远程桌面 CentOS 7
  • Zsh 开发指南(第十四篇 文件读写)
  • 如何选择开源的机器学习框架?
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 小程序 setData 学问多
  • ionic入门之数据绑定显示-1
  • RDS-Mysql 物理备份恢复到本地数据库上
  • 阿里云ACE认证之理解CDN技术
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (poj1.2.1)1970(筛选法模拟)
  • (二)linux使用docker容器运行mysql
  • (剑指Offer)面试题34:丑数
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .net安装_还在用第三方安装.NET?Win10自带.NET3.5安装
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • [CF543A]/[CF544C]Writing Code
  • [FROM COM张]如何解决Nios II SBTE中出现的undefined reference to `xxx'警告
  • [Java开发之路](14)反射机制
  • [LeeCode]—Wildcard Matching 通配符匹配问题
  • [leetcode]_Symmetric Tree
  • [objective-c]关于KVC--KVO--KVB
  • [python 刷题] 2866 Beautiful Towers II
  • [SWPUCTF 2021 新生赛]ez_unserialize
  • [ThinkPHP]Arr返回1
  • [UML]UML系列——类图class的实现关系Realization
  • [Uniapp]携带参数跳转界面(两种方法)
  • [UVA 11825] Hackers' Crackdown
  • [Verilog]有限状态机设计举例
  • [Vue] TodoList 案例