天气预报:
1.天气站可以将每天测量的温度,湿度,气压等等以公告的形式发布出去
2.如果设置开放型API,则第三方的应用也可接入气象站获取数据
3.提供温度、气压和适度的接口。
4.测量数据更新时,要实时的通知第三方
普通方案1:
通过getxxx的方法,直接让第三方接入,并获取相关信息。
当数据变化时,气象站使用dataChange()来更新数据。那么第三方再次获取时,就能得到最新数据。
普通方案2:
当dataChange时,则通知给第三方的update() ,达成推送功能
问题分析:
1.若数据更新,则WeatherDate中的dataChange()里需要所有第三方的update。这样不利于维护,并且不是动态加入,则增加修改成本。
2无法运行时动态地添加到第三方。
3.违反了OCP原则。
观察者模式
允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象。
对象之间多对一依赖的一种设计方案,被依赖的对象叫Subject,依赖的对象Observer,Subject通知Observer变化。
观察者模式结构
1.发布者 (Publisher)
会向其他对象发送值得关注的事件。 事件会在发布者自身状态改变或执行特定行为后发生。 发布者中包含一个允许新订阅者加入和当前订阅者离开列表的订阅构架。
当新事件发生时, 发布者会遍历订阅列表并调用每个订阅者对象的通知方法。 该方法是在订阅者接口中声明的。
2.订阅者 (Subscriber) 接口
声明了通知接口。 在绝大多数情况下, 该接口仅包含一个 update更新方法。 该方法可以拥有多个参数, 使发布者能在更新时传递事件的详细信息。
3.具体订阅者 (Concrete Subscribers)
可以执行一些操作来回应发布者的通知。 所有具体订阅者类都实现了同样的接口, 因此发布者不需要与具体类相耦合。
订阅者通常需要一些上下文信息来正确地处理更新。 因此, 发布者通常会将一些上下文数据作为通知方法的参数进行传递。 发布者也可将自身作为参数进行传递, 使订阅者直接获取所需的数据。
4.客户端 (Client)
会分别创建发布者和订阅者对象, 然后为订阅者注册发布者更新。
观察者模式适合应用场景
1.当一个对象状态的改变需要改变其他对象, 或实际对象是事先未知的或动态变化的时, 可使用观察者模式。
2.当应用中的一些对象必须观察其他对象时, 可使用该模式。 但仅能在有限时间内或特定情况下使用。
实现方式
-
仔细检查你的业务逻辑, 试着将其拆分为两个部分: 独立于其他代码的核心功能将作为发布者; 其他代码则将转化为一组订阅类。
-
声明订阅者接口。 该接口至少应声明一个
update
方法。 -
声明发布者接口并定义一些接口来在列表中添加和删除订阅对象。 记住发布者必须仅通过订阅者接口与它们进行交互。
-
确定存放实际订阅列表的位置并实现订阅方法。 通常所有类型的发布者代码看上去都一样, 因此将列表放置在直接扩展自发布者接口的抽象类中是显而易见的。 具体发布者会扩展该类从而继承所有的订阅行为。
但是, 如果你需要在现有的类层次结构中应用该模式, 则可以考虑使用组合的方式: 将订阅逻辑放入一个独立的对象, 然后让所有实际订阅者使用该对象。
-
创建具体发布者类。 每次发布者发生了重要事件时都必须通知所有的订阅者。
-
在具体订阅者类中实现通知更新的方法。 绝大部分订阅者需要一些与事件相关的上下文数据。 这些数据可作为通知方法的参数来传递。
但还有另一种选择。 订阅者接收到通知后直接从通知中获取所有数据。 在这种情况下, 发布者必须通过更新方法将自身传递出去。 另一种不太灵活的方式是通过构造函数将发布者与订阅者永久性地连接起来。
-
客户端必须生成所需的全部订阅者, 并在相应的发布者处完成注册工作。
观察者模式优缺点
优点:
✔️开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。
✔️你可以在运行时建立对象之间的联系。
缺点:
❌ 订阅者的通知顺序是随机的。
解决天气预报
//观察者
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
//第三方APP
public class CaiYun implements Observer {
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println(this.getClass().getName()+"现在的温度:"+temperature);
System.out.println(this.getClass().getName()+"现在的气压:"+pressure);
System.out.println(this.getClass().getName()+"现在的湿度:"+humidity);
}
}
//第三方APP
public class MoYun implements Observer {
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println(this.getClass().getName()+"现在的温度:"+temperature);
System.out.println(this.getClass().getName()+"现在的气压:"+pressure);
System.out.println(this.getClass().getName()+"现在的湿度:"+humidity);
}
}
//接口,由WeatherData实现
public interface Subject {
//注册观察者
public void registerObserver(Observer o);
//移除观察者
public void removeObserver(Observer o);
//通知所有的观察者
public void notifyObservers();
}
public class WeatherDate implements Subject {
private float temperature;
private float pressure;
private float humidity;
//观察者集合
private ArrayList<Observer> observers;
public WeatherDate(){
observers = new ArrayList<>();
}
public float getTemperature() {
return temperature;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
//通知所有的观察者
public void dataChange(){
notifyObservers();
}
//数据更新
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
//已更新数据 - > 通知观察者
dataChange();
}
//注册观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
//移除一个观察者
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
//遍历并通知观察者
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
observers.get(i).update(this.temperature,this.pressure,this.humidity);
}
}
}
public class Client {
public static void main(String[] args) {
//创建一个天气站
WeatherDate weatherDate = new WeatherDate();
//创建观察者
CaiYun caiYun = new CaiYun();
MoYun moYun = new MoYun();
//注册到天气站中
weatherDate.registerObserver(caiYun);
weatherDate.registerObserver(moYun);
//测试
weatherDate.setData(21F,100F,30.3F);
System.out.println("------");
weatherDate.removeObserver(moYun);
weatherDate.setData(21F,97F,28F);
}
}