前言:观察者模式,顾名思义,是一种一对多的关系,由多个观察对象和被观察对象(主题)组成。主要作用是被观察者(主题)发生改变时,会主动通知观察者对象它自己发生了改变。而观察者对象则可以选择它感兴趣的主题进行观察。
1、简单介绍
这里举一个简单的例子来说明,在以前互联网还没有如此发达的时候,运营商(中国移动、中国电信、中国联通)会给我们提供天气预报服务,我们通过发送短信的方式向运营商订购指定城市的天气信息,订购成功以后,每当天气会发生变化时,我们都会收到来自运营商的天气变化的短信提示。而当我们不再需要天气信息的提示时,我们也可以向运营商发送指定的短信去取消我们的订阅,取消成功以后我们就再也不会收到来自运营商的天气信息的提醒,这其实就是一个最简单的观察者模式。在这里,我们就扮演着观察者的身份,观察我们感兴趣的信息,而运营商就是被观察对象(主题)我们也可以自行决定是否关注它,简单画个图:
图1:用户王五订阅了运营商的天气信息,所以每当有新的天气变化时,运营商都会给王五发送天气信息。而用户张三和李四则对天气信息不敢兴趣所以没有订阅天气信息,因此,运营商也不会给张三和李四发送天气信息。
图2:现在李四出门总是被雨淋,所以现在李四也想提前预知天气信息,故李四给运营商发送订阅天气预报的信息,现在运营商把李四也加入了他们的订阅用户列表,所以一有新的天气信息,就会下发给李四和王五。
图3:张三发现李四订阅了天气预报,自己也想体验一下,故张三给运营商发送了订阅信息,现在运营商把张三也加到了订阅用户列表,而王五觉得自己最近都不出门,天气信息对他而言已经不感兴趣,就取消了订阅,所以现在有新的天气变化时,张三和李四都能收到运营商发送的信息,而王五则不会收到天气信息了。
2、代码实现
现在用户和运营商之间的关系已经描述清楚,我们现在来看看类图吧:
WeatherSubject实现Subject接口,把自己变成一个被观察者(主题),里面的registerObserver()方法和removeObserver()是提供给观察者进行订阅和取消订阅使用的。
每一个观察者对象需实现Observer接口,Observer接口中的update()方法会在Subject的实现类的notification()方法中被调用,来通知观察者数据方法了变化。
2.1、先看看接口的定义:
/**
* 主题的接口
*/
public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notificationAll();
}
/**
* 观察者的接口 update()给主题调用 告知观察者有新的消息
*/
public interface Observer {
void update(String title, String message);
}
/**
* 观察者对象必须实现的接口 用来显示信息
*/
public interface ShowMessages {
void show(String title, String message);
}
2.2、Subject接口实现类:
package com.felix.demo;
import java.util.ArrayList;
import java.util.List;
public class WeatherSubject implements Subject {
//用来存放订阅消息的观察者对象
private List<Observer> list;
private String title, messages;
public WeatherSubject(){
list = new ArrayList<>();
}
/**
* 订阅消息的方法
* @param observer 观察者对象
*/
@Override
public void addObserver(Observer observer) {
list.add(observer);
}
/**
* 取消订阅消息的方法
* @param observer 观察者对象
*/
@Override
public void removeObserver(Observer observer) {
list.remove(list.indexOf(observer));
}
/**
* 通知观察者有新的天气信息
*/
@Override
public void notificationAll() {
for (Observer observer : list){
observer.update(title, messages);
}
}
/**
* 运营商设置天气信息
*/
public void setWeatherData(String title, String messages){
this.title = title;
this.messages = messages;
notificationAll();
}
}
2.3、Observer接口实现类:
package com.felix.demo;
public class WangWuObserver implements Observer,ShowMessages {
/**
* 天气信息发生改变时被调用
*/
@Override
public void update(String title, String message) {
show(title, message);
}
/**
* 展示天气信息
*/
@Override
public void show(String title, String message) {
System.out.println("王五 " +"主题:"+ title + "\n内容:" + message);
}
}
由于张三和李四的实现和王五都是相同的,故这就贴一下王五的代码,主要就是实现Observer接口并重写里面的update()方法,当有新的天气信息时,若王五订阅了运营商的天气信息,该方法就会被回调来告知王五天气信息发生了变化。
2.4、测试类
现在我们就来模拟图1、图2、图3的情况来看看测试类,首先是图1,王五订阅了天气信息,而张三和李四则没有:
package com.felix.demo;
public class Test {
public static void main(String [] args){
WeatherSubject subject = new WeatherSubject();
//三个用户对象
ZhangSanObserver zs = new ZhangSanObserver();
LiSiObserver ls = new LiSiObserver();
WangWuObserver ww = new WangWuObserver();
//王五订阅了天气信息
subject.addObserver(ww);
//张三和李四未订阅天气信息
//subject.addObserver(ls);
//subject.addObserver(zs);
//运营商发布天气信息
//2018.08.01
subject.setWeatherData("天气信息","您好,今天2018年08月01号," +
"深圳天气晴,温度29℃---38℃,出门注意防晒。\n");
//2018.08.02
subject.setWeatherData("天气信息","您好,今天2018年08月02号," +
"深圳天气多云转阵雨,温度28℃---36℃,出门请带伞。\n");
}
}
运行结果:
OK,运行结果如上,仅王五收到了来自运营商发布的天气信息。现在来看看图2的情况,李四也对天气信息感兴趣,所以把自己也变成了观察者,订阅了运营商的天气信息,测试类:
package com.felix.demo;
public class Test {
public static void main(String [] args){
WeatherSubject subject = new WeatherSubject();
//三个用户对象
ZhangSanObserver zs = new ZhangSanObserver();
LiSiObserver ls = new LiSiObserver();
WangWuObserver ww = new WangWuObserver();
//王五订阅了天气信息
subject.addObserver(ww);
//李四也订阅了天气信息
subject.addObserver(ls);
//张三未订阅天气信息
//subject.addObserver(zs);
//运营商发布天气信息
//2018.08.03
subject.setWeatherData("天气信息","您好,今天2018年08月03号," +
"深圳天气多云,温度26℃---37℃,出门注意防晒。\n");
//2018.08.04
subject.setWeatherData("天气信息","您好,今天2018年08月04号," +
"深圳天气小雨,温度28℃---35℃,出门请带雨伞。\n");
}
}
运行结果:
正如我们所料,李四也收到了来自运营商发布的天气信息。现在来看看图3,王五对天气信息不敢兴趣了,而张三却想变成观察者,测试类:
package com.felix.demo;
public class Test {
public static void main(String [] args){
WeatherSubject subject = new WeatherSubject();
//三个用户对象
ZhangSanObserver zs = new ZhangSanObserver();
LiSiObserver ls = new LiSiObserver();
WangWuObserver ww = new WangWuObserver();
//王五订阅了天气信息
subject.addObserver(ww);
//李四也订阅了天气信息
subject.addObserver(ls);
//张三也订阅了天气信息
subject.addObserver(zs);
//王五取消订阅天气信息
subject.removeObserver(ww);
//运营商发布天气信息
//2018.08.05
subject.setWeatherData("天气信息","您好,今天2018年08月05号," +
"深圳天气晴,温度28℃---39℃,出门注意防晒。\n");
//2018.08.06
subject.setWeatherData("天气信息","您好,今天2018年08月06号," +
"深圳天气多云转晴,温度26℃---38℃,出门注意防晒。\n");
}
}
运行结果:
现在,王五再也不会收到来自运营商的推送,除非他再次订阅。