前言
从今日起,陆续分享《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.内部类: