Bootstrap

【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

在这里插入图片描述
在这里插入图片描述

你的设计工具箱

在这里插入图片描述

代码实现

观察者设计模式源码地址

第一版(自己实现)

  • 主题
public interface Subject {
    void registerObserver(Observer o);

    void removeObserver(Observer o);

    void notifyObservers();
}
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();
    }
}
  • 观察者
public interface Observer {
    void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
    void display();
}
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();

    }
}
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();

    }
}
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();

    }
}
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();

    }
}

  • 测试
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();
    }
}

运行结果:

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接口
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接口
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();
        }
    }
}
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();
        }
    }
}

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();
        }
    }
}
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();
        }
    }
}
  • 测试(结果并不是按照预期顺序打印)
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();
    }
}

运行结果:

酷热指数展示实现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

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!");
        }
    }
}
  • 运行结果
控制台输出如下结果:
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.内部类:

;