观察者模式
观察者模式(Observer Pattern):也叫做发布订阅模式,其定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新,观察者模式属于行为型模式。
这里观察者模式有两种:push 模式,pull 模式
push 模式
示例:我们以获取天气预报中的气温来举例说明
package cn.liangyy.observer;
/**
* 观察者接口
*/
public interface Observer {
void update(float temperature); //更新天气信息
}
复制代码
package cn.liangyy.observer;
/**
* 管理观察者
*/
public interface Subject {
void registerObserver(Observer o); //注册观察对象
void removeObserver(Observer o); //移除观察对象
void notifyObservers(); //通知观察对象
}
复制代码
package cn.liangyy.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 被观察者
* 天气数据
*/
public class WeatherData implements Subject {
//观察者不止一个,所以用list进行维护
private List<Observer> observers;
//温度
private float temperature;
public void setMessurements(float temperature){
this.temperature = temperature;
//气温信息发生变化时,通知所有观察者
notifyObservers();
}
public WeatherData(){
//初始化list
this.observers = new ArrayList<>();
}
/**
* 注册观察者
* @param o
*/
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
/**
* 移除观察者
* @param o
*/
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0){
observers.remove(i);
}
}
/**
* 通知所有观察者
*/
@Override
public void notifyObservers() {
//遍历所有的观察者
for (Observer observer : observers){
//通知观察者更新数据信息
observer.update(temperature);
}
}
}
复制代码
package cn.liangyy.observer;
/**
* 观察者类
*/
public class WeatherDisplay implements Observer {
//维护观察者
private Subject subject;
//温度
private float temperature;
public WeatherDisplay(Subject subject){
//注册监听对象
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update(float temperature) {
//当被观察者气温发生变化会调用这个方法,也就等于更新了观察者对象的数据
this.temperature = temperature;
}
public void display(){
System.out.println("当前最新的温度是:"+temperature);
}
}
复制代码
package cn.liangyy.observer;
/**
* 观察者模式-测试
*/
public class TestWeather {
public static void main(String[] args) {
//天气数据即被观察者
WeatherData weatherData = new WeatherData();
//天气展示即观察者
WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData);
//被观察者数据发生变化了,其内部会通知观察者
weatherData.setMessurements(16.2f);
//查看观察者是否获取到了最新温度数据
weatherDisplay.display();
}
}
复制代码
由上述例子可以看到,我们只是设置了 WeatherData 对象中的温度,但是 WeatherDisplay 中也实时改变了温度,这就是观察者模式。那么这种 push 模式,是观察者主动推的数据给观察者,不论观察者是不是需要数据,都会被推送,那么由一个问题:就是数据量很大,但是我只要其中的一部分数据,该怎么办呢??
pull 模式
示例:我们以空间好友自行去获取好友发表在空间的动态来举例说明(这里利用 JDK 的观察者模式来实现一个 pull 类型的观察者模式)
package cn.liangyy.observer.pull;
/**
* 空间动态类
* 记录动态信息
*/
public class Trends {
//发表动态的用户昵称
private String nickName;
//发表的动态内容
private String content;
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
复制代码
package cn.liangyy.observer.pull;
import java.util.Observable;
/**
* 空间类(被观察者)
*/
public class Zone extends Observable {
//发表动态
public void publishTrends(Trends trends){
System.out.println(trends.getNickName()+" 发表了一个动态【"+trends.getContent()+"】");
//占位,只是设置一个标记说明数据改变了
setChanged();
//通知所有观察者
notifyObservers(trends);
}
}
复制代码
package cn.liangyy.observer.pull;
import java.util.Observable;
import java.util.Observer;
/**
* 好友类(观察者)
*/
public class Friends implements Observer {
//看动态的人的名字
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
*/
@Override
public void update(Observable o, Object arg) {
//获取(空间)被观察者数据
Trends trends = new Trends();
if (null != arg && arg instanceof Trends){
trends = (Trends) arg;
}
System.out.println(this.getName()+",您好!您收到了来自 "+trends.getNickName()+" 的一条动态【"+trends.getContent()+"】,快去点赞吧!");
}
}
复制代码
package cn.liangyy.observer.pull;
/**
* 观察者模式-测试
*/
public class TestObserver {
public static void main(String[] args) {
Zone zone = new Zone();
//观察者,即查看好友动态的人
Friends friends = new Friends();
friends.setName("亚索");
//被观察者,即发送动态的人
Trends trends = new Trends();
trends.setNickName("瑞雯");
trends.setContent("此生唯爱亚索!");
//注册观察者
zone.addObserver(friends);
//发布动态
zone.publishTrends(trends);
}
}
复制代码
可以看到,当动态发布之后,好友立即就收到了动态,而这里我们是主动去获取数据的,即Friends
类的 update()方法。我们可以根据需求拿该拿的数据。
JDK 自带的观察者模式的局限性
观察者模式的使用场景‘一般适用于需要实时监听数据的场景,比如事件监听器等。
观察者模式的优点观察者和被观察者之间建立一个抽象的耦合,要扩展观察者只需要新建观察者并注册进去就可以了,扩展性好。
观察者模式的缺点
评论