Head First Design Patterns - 观察者模式
2024-01-01 12:29:39
观察者模式
观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式是一种对象行为型模式。
场景
- 很多用户都订阅了某一公众号,当该公众号更新时,所以用户都会收到消息。该公众号叫做【主题,Subject】,订阅者叫做【观察者,Observer】。
- 气象台会将每日更新的天气数据,如温度,气压等,下发给第三方的网站进行显示。气象台被称为【主题,Subject】,不同的第三方网站叫做【观察者,Observer】。书上也是以气象台为例。
角色
- 主题(Subject):指被观察的对象。可以为接口,也可以为抽象类,书中的例子为接口。该接口中一般定义了registerObserver()、removeObserver()、notifyObservers()等方法,作用分别为观察者注册、移除观察者、通知所有的观察者。该类中还会维护一个数据结构来保存所有观察者的引用,书中例子用了List<>,但为了线程安全,一般都用vector<>。
- 具体主题(ConcreteSubject):因为书中的主体定义为接口,因此具体主题类是实现了该主题接口。具体主题也会定义自己的一些逻辑。
- 观察者(Observer):为接口。定义了update()方法,主题类调用该方法将消息通知到观察者。
- 具体观察者(ConcreteObserver):维护了一个具体主题对象的引用,方便利用主题的方法进行注册等工作。具体观察者作为Observer的实现类,还实现了update方法。
上述角色可以参考后面的类图。
气象台的实现
气象台的改变逻辑会放在measurementsChanged方法中。
不用设计模式的实现
public class WeatherData {
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
// 可以理解为更新不同接入气象台网站的显示
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
}
}
以上实现违背了一些设计原则:
- 是针对具体的实现,而不是针对接口的实现。没有办法在不修改代码的情况下添加或者移除一些显示元素。
- update方法中都是传入几个相同参数,属于主体统一推到观察者的模式,没有考虑到网站真正需要什么。
- 属于硬编码。
观察者模式的实现
类图
示例
- 定义主题接口
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
- 实现具体的主题
public class WeatherData implements Subject{
List<Observer> observerList;
private float temp; //温度
private float humidity; //湿度
private float pressure; //压强
public WeatherData() {
observerList = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o) {
observerList.add(o);
}
@Override
public void removeObserver(Observer o) {
observerList.remove(o);
}
@Override
public void notifyObserver() {
observerList.forEach(Observer::update);
}
public void measurementsChanged() {
notifyObserver();
}
public void setMeasurements(float temperatue, float humidity, float pressure) {
this.temp = temperatue;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
- 定义观察者接口
public interface Observer {
void update();
}
- 定义展示接口(书中业务的实现,与设计模式没有联系)
public interface DisplayElement {
public void display();
}
- 定义具体的观察者
public class CurrentConditionDisplay implements Observer, DisplayElement{
private float temp;
private float humidity;
private float pressure;
private WeatherData weatherData;
public CurrentConditionDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update() {
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity(); // 观察者需要什么,可以从主题中拉取,是属于拉的模式
this.pressure = weatherData.getPressure();
this.display();
}
@Override
public void display() {
System.out.println("current display:" + temp + " " + humidity + " " + pressure);
}
- 客户端
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay currentConditionDisplay =
new CurrentConditionDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
}
输出:
current display:80.0 65.0 30.4 // 例子中只定义了一个观察者
观察者模式所涉及到的设计原则
- 封装变化。该例子中,变化的是主题的状态、观察者的数量和类型。
- 针对接口编程而不是针对实现编程。主题跟踪具体的观察者,而观察者通过主题接口来注册并通知。
- 松耦合。降低了主题和观察者之间的耦合关系,使代码更能应对场景的变化。
应用
spring中的事件驱动模型,具体参考观察者模式
文章来源:https://blog.csdn.net/kwb2015130086/article/details/135322986
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!