观察者模式
简介
定义对象间的一种一对多的关系。当一个对象状态改变时,所有依赖于它的对象的状态都得到通知且被自动更新。
也叫发布订阅模式,包含推模式和拉模式,也就相当于是广播和特定通知。
推模式:广播模式,目标对象主动的向观察者推送目标的详细信息。
拉模式:目标对象通知观察者时,只传递少量的信息,如果需要具体信息的话就送目标对象中拉取
适合的场景
- 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
- 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
观察者模式的演示代码
角色介绍
气象观测:目标对象
女朋友:观察者1,需要获取天气情况来决定明天去不去约会
Mum:观察者2,需要根据天气情况来决定明天去不是天安门
流程介绍
因此监听者必须实现获得目标对象发送消息的接口。
编码
1.编写观察者接口,必须要有更新接口(推和拉)
/*
*
* 观察者接口,定义一个更新接口给目标发生改变时被通知的对象
* */
public interface Observer {
//更新接口,Subject传入的目标对象,推模式
public void update(WeatherSubject weatherSubject);
//拉模型,可以看到一个直接传的是对象的引用,拉模型根据需求传递
public void updatePull(String content);
}
- 编写目标对象,使用一个集合来装观察者对象,并且实现订阅者的添加,删除,和遍历
/*
* 目标对象,观察它的观察者,并提供注册和删除观察者的接口
* */
public class WeatherSubject {
//用来保存订阅天气的人
private List<Observer> observers = new ArrayList<>();
//添加订阅天气的人
public void attach(Observer observer) {
observers.add(observer);
}
//删除指定订阅者
public void detach(Observer observer) {
observers.remove(observer);
}
//通知所有已经订阅天气的人,推模型(广播的方法)
protected void notifyObservers() {
for(Observer obs:observers) {
obs.update(this);
}
}
//拉模式通知
protected void notifyObservers(String content) {
for(Observer obs:observers) {
obs.updatePull(content);
}
}
}
- 编写目标类的具体实现,目的就是一旦有消息就notify
/*
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
*
* */
public class ConcreteWeatherSubject extends WeatherSubject {
//获取天气内容信息
private String weatherContentPush;
//获取天气信息
private String weatherContentPull;
public String getSubjectState() {
return weatherContentPush;
}
//推模型,推送全部信息
public void setSubjectState(String subjectState) {
this.weatherContentPush = subjectState;
//有内容了,说明天气更新了,通知所有订阅者
this.notifyObservers();
}
public String getWeatherContentPull() {
return weatherContentPull;
}
//拉模型,按需推送
public void setWeatherContentPull(String weatherContentPull) {
this.weatherContentPull = weatherContentPull;
this.notifyObservers(weatherContentPull);
}
}
- 具体的观察者对象
/*
* 具体的观察者对象,实现更新方法,使自身的状态和目标保持一致
* */
public class ConcreteObserver implements Observer {
//1.观察者名字
private String observerName;
//2.天气状态
private String weatherContent;
//3.不同人的不同提示信息
private String remindThing;
//获取目标类的状态同步到观察的状态中
@Override
public void update(WeatherSubject weatherSubject) {
weatherContent = ((ConcreteWeatherSubject)weatherSubject).getSubjectState();
System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
}
//拉模型
@Override
public void updatePull(String content) {
weatherContent = content;
System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
}
get XXXX
set XXXX
}
- Test
public class Test {
public static void main(String[] args) {
//1.创建目标
ConcreteWeatherSubject weather = new ConcreteWeatherSubject();
//2.创建观察者
ConcreteObserver obsGirl = new ConcreteObserver();
obsGirl.setObserverName("GirlFriend");
obsGirl.setRemindThing("第一次约会,不要迟到");
ConcreteObserver obsMum = new ConcreteObserver();
obsMum.setObserverName("Mum");
obsMum.setRemindThing("明天去北京天安门");
//3.注册观察者
weather.attach(obsMum);
weather.attach(obsGirl);
//4.目标发布天气
//推
weather.setSubjectState("明天天气晴朗");
//拉
weather.setWeatherContentPull("明天天气很好");
}
}
注意的是,要时间分明,Mum和GirlFriend订阅的只是天气,因此订阅者需要提供除了天气的东西,目标对象(气象台)要提供天气,然后公布天气后通知给订阅者们。