【HeadFirst系列之HeadFirst设计模式】第3天之观察者模式

前言

从今日起,陆续分享《HeadFirst设计模式》的读书笔记,希望能够帮助大家更好的理解设计模式,提高自己的编程能力。

设计模式本质上就是前人比较成熟的经验和智慧。他们遇到过相同的问题,也顺利地解决了这些问题。

跟随前人的脚步,可以少走弯路,也可以站在巨人的肩膀上看得更远。

使用模式最好的方式是:"把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用它们。"以往是代码复用,现在是经验复用。

今天要分享的是【设计模式入门之观察者模式】,希望对大家有所帮助。

书籍精彩内容

气象观测站

这个图片有点像我们平时接到新需求的BRD,然后产品经理会把它转换为PRD,最后我们开发人员根据PRD进行开发。

需求描述:

1.新建应用

2.获取气象站数据并在应用展示

3.应用可以实时获取气象站数据并更新,同时展示

4.应用具有扩展性,可以添加更多的展示方式

。。。

WeatherData对象从气象站取得数据(温度、湿度、气压),在显示装置展示。

气象数据类

WeatherData类有如下方法:

getTemperature():获取温度

getHumidity():获取湿度

getPressure():获取气压

measurementsChanged():当气象站测量数据更新时,调用此方法

。。。

我们的代码主要在measurementsChanged()方法中编写。

WeatherData对象有3个方法,分别获取温度、湿度、气压

WeatherData对象有一个measurementsChanged()方法,当气象站测量数据更新时,调用此方法

有三种显示装置:目前天气状况、天气统计、天气预报

未来可能会有第4,第5种,甚至更多种显示装置

第一次尝试气象站

针对实现编程,而不是接口编程,耦合性太高。

改变的地方需要封装起来

不要针对实现编程

认识观察者模式

报纸的订阅涉及三个对象:报社、订阅者、报纸

订阅者订阅报纸,报社发布新报纸,订阅者收到新报纸

订阅者取消订阅,报社不再发送新报纸

主题(Subject):出版者

观察者(Observer):订阅者

观察者模式的一天


鸭子申请成为观察者

老鼠申请从观察者列表移除

五分钟短剧


后泡沫时期的两名软件工程师通过猎头找工作

猎头把求职者加入求职者清单,一有消息就通知求职者

这里猎头是主题,求职者是观察者

定义观察者模式


观察者模式定义了对象之间的一对多依赖,

这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

松耦合

其实dubbo就是一个松耦合的例子,面向接口编程,调用方不用关注实现方实现细节。

规划气象站

设计气象站

实现气象站



测试气象站


围炉夜话:主题与观察者


Java内置的观察者模式


幕后花絮

重做目前状况布告板


测试驱动


观察者与Swing


你的设计工具箱

代码实现

观察者设计模式源码地址

第一版(自己实现)

  • 主题
java 复制代码
public interface Subject {
    void registerObserver(Observer o);

    void removeObserver(Observer o);

    void notifyObservers();
}
java 复制代码
public class WeatherData implements Subject {
    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

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

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

    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i > 0) {
            observers.remove(i);
        }

    }

    @Override
    public void notifyObservers() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = (Observer) observers.get(i);
            observer.update(temperature, humidity, pressure);
        }

    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
  • 观察者
java 复制代码
public interface Observer {
    void update(float temp, float humidity, float pressure);
}
java 复制代码
public interface DisplayElement {
    void display();
}
java 复制代码
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;

    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }

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

    }
}
java 复制代码
public class StatisticsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;

    private Subject weatherData;

    public StatisticsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("天气统计展示实现");
    }

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

    }
}
java 复制代码
public class ForecastDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;

    private Subject weatherData;

    public ForecastDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("天气预报展示实现");
    }

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

    }
}
java 复制代码
public class HeatIndexDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;

    private Subject weatherData;

    public HeatIndexDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println("酷热指数展示实现");
    }

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

    }
}
  • 测试
java 复制代码
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);
        weatherData.setMeasurements(80, 65, 30.4f);
        System.out.println();
        weatherData.setMeasurements(82, 70, 29.2f);
        System.out.println();
        weatherData.setMeasurements(78, 90, 29.2f);
        System.out.println();
    }
}

运行结果:

bash 复制代码
Current conditions: 80.0F degrees and 65.0% humidity
天气统计展示实现
天气预报展示实现
酷热指数展示实现

Current conditions: 82.0F degrees and 70.0% humidity
天气统计展示实现
天气预报展示实现
酷热指数展示实现

Current conditions: 78.0F degrees and 90.0% humidity
天气统计展示实现
天气预报展示实现
酷热指数展示实现

第二版(用JDK自带的实现,实现了Observable和Observer接口,都位于java.util包下)

  • 主题,实现了java.util.Observable接口
java 复制代码
public class WeatherDataV2 extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherDataV2() {

    }


    public void measurementsChanged() {
        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;
    }
}
  • 观察者,实现了java.util.Observer接口
java 复制代码
public class CurrentConditionsDisplayV2 implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;


    public CurrentConditionsDisplayV2(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions:" + temperature
                + "F degrees and " + humidity + "% humidity");

    }


    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherDataV2) {
            WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
            this.temperature = weatherDataV2.getTemperature();
            this.humidity = weatherDataV2.getHumidity();
            display();
        }
    }
}
java 复制代码
public class StatisticsDisplayV2 implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;


    public StatisticsDisplayV2(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("天气统计展示实现V2");

    }


    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherDataV2) {
            WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
            this.temperature = weatherDataV2.getTemperature();
            this.humidity = weatherDataV2.getHumidity();
            display();
        }
    }
}
java 复制代码
public class ForecastDisplayV2 implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;


    public ForecastDisplayV2(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("天气预报展示实现V2");

    }


    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherDataV2) {
            WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
            this.temperature = weatherDataV2.getTemperature();
            this.humidity = weatherDataV2.getHumidity();
            display();
        }
    }
}
java 复制代码
public class HeatIndexDisplayV2 implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;


    public HeatIndexDisplayV2(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("酷热指数展示实现V2");

    }


    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherDataV2) {
            WeatherDataV2 weatherDataV2 = (WeatherDataV2) o;
            this.temperature = weatherDataV2.getTemperature();
            this.humidity = weatherDataV2.getHumidity();
            display();
        }
    }
}
  • 测试(结果并不是按照预期顺序打印)
java 复制代码
public class WeatherStationV2 {
    public static void main(String[] args) {
        WeatherDataV2 weatherDataV2 = new WeatherDataV2();
        CurrentConditionsDisplayV2 currentDisplayV2 = new CurrentConditionsDisplayV2(weatherDataV2);
        StatisticsDisplayV2 statisticsDisplayV2 = new StatisticsDisplayV2(weatherDataV2);
        ForecastDisplayV2 forecastDisplayV2 = new ForecastDisplayV2(weatherDataV2);
        HeatIndexDisplayV2 heatIndexDisplayV2 = new HeatIndexDisplayV2(weatherDataV2);
        weatherDataV2.setMeasurements(80, 65, 30.4f);
        System.out.println();
        weatherDataV2.setMeasurements(82, 70, 29.2f);
        System.out.println();
        weatherDataV2.setMeasurements(78, 90, 29.2f);
        System.out.println();
    }
}

运行结果:

bash' 复制代码
酷热指数展示实现V2
天气预报展示实现V2
天气统计展示实现V2
Current conditions:80.0F degrees and 65.0% humidity

酷热指数展示实现V2
天气预报展示实现V2
天气统计展示实现V2
Current conditions:82.0F degrees and 70.0% humidity

酷热指数展示实现V2
天气预报展示实现V2
天气统计展示实现V2
Current conditions:78.0F degrees and 90.0% humidity

Java wing demo

java 复制代码
public class SwingObserverExample {
    JFrame frame;

    public static void main(String[] args) {
        SwingObserverExample example = new SwingObserverExample();
        example.go();
    }

    public void go() {
        frame = new JFrame();
        JButton button = new JButton("Should I do it?");
        button.addActionListener(new AngelListener());
        button.addActionListener(new AngelListener());
        frame.getContentPane().add(BorderLayout.CENTER, button);
    }

    class AngelListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            System.out.println("Don't do it, you might regret it!");
        }
    }

    class DevilListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Come on,do it!");
        }
    }
}
  • 运行结果
bash 复制代码
控制台输出如下结果:
2025-01-19 13:31:40.726 java[80134:9279486] +[IMKClient subclass]: chose IMKClient_Modern
2025-01-19 13:31:40.726 java[80134:9279486] +[IMKInputSession subclass]: chose IMKInputSession_Modern

图形化页面还未点击,就消失了

实际运用场景

  • 生活场景

    1.家里的摄像头监控,有异常就手机通知我

    2.订阅了某个公众号,有新文章就通知我

    3.抢火车票,有票就通知我

    4.电商购物,订阅到货提醒,订阅秒杀提醒等

    5.。。。

  • 代码场景

    1.mq消息队列

    2.jdk自带(Oberver,Swing)

    3.。。。

套公式

新需求是什么,是否有需要实时监听的场景

如果有确认主题(被监听者),确认观察者(监听者),

那么就可以套用观察者模式了

有趣的事

1.家里冰箱贴改为:代码贴主题,既可以当冰箱贴,又可以玩类似消消乐类似游戏

总结

1.设计原则:

  • 找出程序中会变化的方面,然后将其和固定不变的方面相分离。【封装变化】
  • 针对接口编程,不针对实现编程
  • 多用组合,少用继承

2.观察者模式:

  • 定义
    观察者模式--在对象之间定义一对多的依赖,这样一来,省一个对象改 变状态,依赖它的对象都会收到通知, 并自动更新。
  • MVC模式
    一个新的模式,以松耦合方式在一系列对象之间沟通状态。我们目前还没看到观察者模式的代表人物--MVC,以后就会看到了。

3.类图:

4.this关键字:

5.内部类:

相关推荐
Mr_sun.几秒前
Maven下载&配置
java·maven
咖啡の猫27 分钟前
观察者模式
观察者模式·设计模式
工业互联网专业1 小时前
基于springboot+vue的城市公交查询系统
java·vue.js·spring boot·毕业设计·源码·课程设计
G3G41 小时前
Maven项目中没有.iml文件
java·maven·intellij-idea
落霞与孤鹭齐飞。。2 小时前
SSM宠物医院信息管理系统
java·开发语言·mysql·毕业设计·课程设计
上海拔俗网络3 小时前
“AI 大模型内容安全审核软件系统:守护网络世界的卫士
java·团队开发
m0_748251083 小时前
PHP For 循环
android·java·php
m0_748254473 小时前
记录一下在Win上搭建RustDesk
java
我要学编程(ಥ_ಥ)3 小时前
初始JavaEE篇 —— 快速上手 SpringBoot
java·spring boot·spring·java-ee
摸鱼也很难3 小时前
安全开发 JavaEE && 反射机制 && 对象 成员变量 构造方法 成员方法 && 攻击链
java·java-ee·java安全