资讯专栏INFORMATION COLUMN

Java 设计模式之观察者模式

jsummer / 1180人阅读

摘要:三内置观察者模式了解内置观察者模式包内包含最基本的类与接口,这和上面的接口与接口很类似。根据具体的需求,如果内置的观察者模式不能满设计,那么我们可以像刚开始那样自己实现一套观察者模式。参考资料设计模式

一、了解观察者模式

1.1 什么是观察者模式

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。

典型的问题比如报社,只要你是他们的订户,他们每次有新报纸出版时,就会向你这送来,当你不想要看报纸时,取消订阅,他们就不会再给你送报纸。

1.2 观察者模式组成结构

抽象主题 (Subject):抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

具体主题 (ConcreteSubject):该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

抽象观察者 (Observer):是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

具体观察者 (ConcrereObserver):实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

1.3 观察者模式 UML 图解

二、观察者模式具体应用

2.1 问题描述

气象观测站系统:该系统中包含三部分,分别是气象站 (获取实际气象数据的物理装置)、WeatherData 对象 (追踪气象站的数据,并更新布告板) 和布告板 (显示天气状况给用户看,布告板共有两个,分别显示当前的温度以及对天气进行预告)。

2.2 问题分析

我们想要使用观察者模式去解决这个问题,首先要分析出什么是主题,什么是观察者,问题的关键是找出一对多依赖关系。这里 WeatherData 类正如所说的“一”,而“多”是用于显示天气情况的布告板。

WeatherData 是有状态的对象,它包括了温度、湿度和气压,而这些值都会变化,当这些值改变时,必须通知布告板,好让它们显示最新的数据。所以把 WeatherData 类作为主题,布告板作为观察者。

2.3 问题分析设计图

2.4 代码实现

PS:代码模块较多,建议将这些代码拷下来运行一遍。

抽象主题接口 Subject

package com.jas.observer;

public interface Subject {

    /**
     *  注册观察者
     *  
     * @param observer 观察者对象
     */
    void registObserver(Observer observer);

    /**
     *  移除观察者
     *
     * @param observer 观察者对象
     */
    void removeObserver(Observer observer);

    /**
     * 当主题状态改变时,这个方法会被调用,通知所有的观察者
     */
    void notifyObservers();
}

抽象观察者接口 Observer

package com.jas.observer;

public interface Observer {

    /**
     * 当气象观测值改变时,主题会把这些状态值作为参数,传送给观察者
     * 
     * @param temp 温度
     * @param humidity  湿度
     * @param pressure  压力
     */
    void update(float temp, float humidity, float pressure);
}

布告信息接口 DisplayElement

package com.jas.observer;

public interface DisplayElement {
    void display();
}

具体主题类 WeatherData

package com.jas.observer;

import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {
    private float temperature;
    private float humidity;
    private float pressure;
    private List list = new ArrayList();    //使用集合保存所有的观察者对象
    
    
    @Override
    public void registObserver(Observer observer) {
        list.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int i = list.indexOf(observer);
        if(i >= 0 && i < list.size()){
            list.remove(i);
        }
    }

    @Override
    public void notifyObservers() {
        for (int i = 0; i < list.size(); i++) {    //遍历集合中所有观察者对象
            Observer observer = list.get(i);
            observer.update(temperature,humidity,pressure);    //调用观察者的 update() 方法
        }
    }

    /**
     * 当气象站的数据得到更新后,通知观察者,调用 notifyObservers() 方法
     */
    public void measurementsChanged(){
        notifyObservers();
    }

    /**
     * 当气象站数据改变后,设置新的数据值,并调用 measurementsChanged() 方法
     * 
     * @param temperature  温度
     * @param humidity  湿度
     * @param pressure  气压
     */
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

具体观察者对象,当前天气信息类 CurrentConditionsDisplay

package com.jas.observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherData;

    /**
     * 通过构造函数将当前观察者注册给具体主题对象
     * 
     * @param weatherData 主题对象
     */
    public CurrentConditionsDisplay(Subject weatherData){
        this.weatherData = weatherData;
        weatherData.registObserver(this);
    }
    
    /**
     * 布告板信息展示
     */
    @Override
    public void display() {
        System.out.println("Current conditions list : " + "温度 = " + 
                temperature + ", 湿度 = " + humidity + ", 气压 = " + pressure);
    }

    /**
     * 更新信息
     * 
     * @param temp 温度
     * @param humidity  湿度
     * @param pressure  压力
     */
    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
}

具体观察者对象,预测天气信息类 ForecastDisplay(简单将数据减一)

package com.jas.observer;

public class ForecastDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private float pressure;
    private Subject weatherData;

    /**
     * 通过构造函数将当前观察者注册给主题对象
     *
     * @param weatherData 主题对象
     */
    public ForecastDisplay(WeatherData weatherData){
        this.weatherData = weatherData;
        weatherData.registObserver(this);
    }
    
    @Override
    public void display() {
        System.out.println("Forecast conditions list : " + "温度 = " +
                (temperature - 1.0) + ", 湿度 = " + (humidity - 1.0) + ", 气压 = " + (pressure - 1.0));
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
}

气象站类 WeatherStation

package com.jas.observer;

public class WeatherStation {
    public static void main(String[] args) {
        
        WeatherData weatherData = new WeatherData();
        
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        
        //当具体主题对象数据发生变化,所有依赖者 (观察者) 都会实现自动数据更新
        weatherData.setMeasurements(18,65,30);
    }
}

    /**
     * 输出
     * Current conditions list : 温度 = 18.0, 湿度 = 65.0, 气压 = 30.0
     * Forecast conditions list : 温度 = 17.0, 湿度 = 64.0, 气压 = 29.0
     */

2.5 自定义观察者模式总结

观察者模式可以轻松实现松耦合,因为主题并不需要知道观察者的具体类是谁,做了些什么,并且我们可以在任何时候新增观察者。由于一个主题可能对应多个观察者,所以当某一个观察者出现问题时,可能导致其他的观察者也不能正常工作。因此在一定程度上,存在着效率问题。

三、Java 内置观察者模式

3.1 了解 Java 内置观察者模式

java.util 包内包含最基本的Observable 类Observer 接口,这和上面的 Subject 接口与 Observer 接口很类似。Observable 类与 Observer 接口使用起来更方便,因为许多的功能已经提供了。

3.2 Java 内置观察者模式如何运作

(1)如何把对象定义为观察者?

实现观察者 (Observer) 接口,调用任何 Observable 对象的 addObserve() 方法。当不想要当观察者时,调用 deleteObserve() 方法。

(2)可观察者如何发送通知?

先调用 setChanged() 方法,标记状态已经被改变的事实。

调用 notifyObservers()notifyObservers(Object arg) 方法。

(3)观察者如何接收通知?

同以前一样,观察者实现了 update(Observable o, Object arg) 方法,只是方法签名不太一样。

3.3 重写气象观测站系统

主题类 WeatherData

package com.jas.jdk.observer;

import java.util.Observable;

public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;
    
    public void measurementsChanged(){
        //在通知所有观察者之前,先调用 setChanged() 方法,用来表示状态已经改变
        setChanged();
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    public float getTemperature(){
        return temperature;
    }
    
    public float getHumidity(){
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

具体观察者对象,当前天气信息类 CurrentConditionsDisplay

package com.jas.jdk.observer;

import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private float pressure;
    private Observable observable;

    /**
     * 通过构造函数,将当前对象记录为观察者
     * 
     * @param observable 主题对象
     */
    public CurrentConditionsDisplay(Observable observable){
        this.observable = observable;
        observable.addObserver(this);
    }
    
    @Override
    public void display() {
        System.out.println("Current conditions list : " + "温度 = " +
                temperature + ", 湿度 = " + humidity + ", 气压 = " + pressure);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof WeatherData){
            WeatherData weatherData = (WeatherData) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            this.pressure = weatherData.getPressure();
            display();
        }
    }
}

气象站类 WeatherStation(同上)

package com.jas.jdk.observer;


public class WeatherStation {
    public static void main(String[] args) {
        
        WeatherData weatherData = new WeatherData();
        
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        //ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        
        weatherData.setMeasurements(80,65,30.4f);
    }
}

     /**
     * 输出
     * Current conditions list : 温度 = 80.0, 湿度 = 65.0, 气压 = 30.4
     */

3.4 Java 内置观察者模式总结

Java 内置的观察者模式允许观察者有选择的获取数据,而不是主题对象强制将更新数据全部推送个每个观察者。

Observable 是一个类,并不是一个接口,这意味着你继承它的同时,不能再继承其他的类。在 Observable 类中 setChanged() 方法被保护了起来 (protected),除非你继承该类,否则你无法创建 Observable 实例组合到你自己的对象中来。所以它违反了一个原则:“多用组合,少用继承”。

还有一点需要要注意的是:内置的观察者模式,观察者被通知的顺序并不是唯一的 (上面只定义了一个观察者),有时候并不能达到我们一开始的目的,你可以定义多个观察者验证一下。

根据具体的需求,如果 Java 内置的观察者模式 API 不能满设计,那么我们可以像刚开始那样自己实现一套观察者模式。

参考资料

《Head First 设计模式》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77238.html

相关文章

  • Java设计模式察者模式

    摘要:观察者模式的使用场景比如你微博关注了一个人,那么这个人发布的微博就会推送到你这。 Java设计模式之观察者模式 一直想写一篇学习观察者模式的总结没有契机,今天学习阻塞队列的原理时候看到在实现生产者消费者的时候用到了通知模式,就是所谓的观察者模式,正好顺便整理一下。 1. 简介 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更...

    haitiancoder 评论0 收藏0
  • 前端学习笔记察者模式

    摘要:观察者模式也称发布订阅模式它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态举个生活比较常见常见的例子比如你去面试之后,面试官看你表现不错,最后会跟你要联系方式,以便之后可以联系你。 观察者模式也称发布-订阅模式,它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态 举个生活比较常见常见的例子,比如你去面试之后,...

    tommego 评论0 收藏0
  • 设计模式察者设计模式

    摘要:关键概念理解观察者设计模式中主要区分两个概念观察者指观察者对象,也就是消息的订阅者被观察者指要观察的目标对象,也就是消息的发布者。 原文首发于微信公众号:jzman-blog,欢迎关注交流! 最近补一下设计模式相关的知识,关于观察者设计模式主要从以下几个方面来学习,具体如下: 什么是观察者设计模式 关键概念理解 通知观察者的方式 观察者模式的实现 观察者模式的优缺点 使用场景 下面...

    NotFound 评论0 收藏0
  • 设计模式系列察者模式

    本文从jdk内置的观察者模式来介绍观察者模式。业务场景:当老师进门的时候,班长带头叫老师好,然后全班同学一起交老师好因为太简单,直接上代码 班长继承自Observable package Observer; import java.util.Observable; import java.util.Observer; public class Monitor extends Observabl...

    Edison 评论0 收藏0
  • Java设计模式察者模式详解

    摘要:观察者模式,是一对多的关系,一个主题对应多个观察者,当这个主题发生变化的时候,所有观察着这个主题的观察者都会接收到通知来获悉主题的变化。这就是使用的观察者模式,下面就让我们用代码实现观察者模式。 观察者模式,是一对多的关系,一个主题对应多个观察者,当这个主题发生变化的时候,所有观察着这个主题的观察者都会接收到通知来获悉主题的变化。 在现实中我们也会遇到许许多多应用观察者模式的行为,比如...

    dayday_up 评论0 收藏0

发表评论

0条评论

jsummer

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<