观察者模式定义了对象之间一对多的关系,这样一来,当一个对象状态改变时,它的所有依赖者都会收到通知并自动更新。
我们可以举个例子,比如报纸或者杂志的订阅,我们读者属于订阅者,报纸或者杂志属于出版者,那么:
- 向某家报纸社订阅报纸,只要他们有新报纸出版,就会给读者送过去,只要你是他的订阅者,那么你就一直会收到新报纸。
- 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸过来了。
- 只要报纸社还在运营,就会一直有人向他们订阅报纸或者取消订阅报纸。
以上就是一个典型的适用于观察者模式的案例,我们画个图说明一下:
对于这种一对多的关系,我们常用 Subject 和 Observer 接口的类设计来进行实现:
- Subject:这是主题接口,对象使用此接口注册为观察者,或者自己从观察者中删除
- Observer:所有潜在的观察者都必须实现观察者接口,这个接口只有 update() 一个方法,当主题状态改变时它被调用
- ConcreteSubject:一个具体主题总是实现主题接口,除了注册和撤销方法之外,具体主题还实现了 notifyObserver() 方法,此方法用于在改变状态时更新所有当前的观察者
- ConcreteObserver:具体的观察者可以是实现此接口的任意类,观察者必须注册具体主题,以便接收更新
观察者模式提供一种对象设计,让主题和观察者之间松耦合。我们可以在代码中随时实现一个实现了 Observer 接口的新观察者,运行时也可以使用新观察者取代现有的观察者,主题不会收到任何的影响,同样可以在任何时候删除某些观察者。
下面我们以微信公众号为例子,假设微信用户就是观察者,微信公众号是发布者,有多个用户关注了 NBA 这个公众号,当这个公众号更新时就会通知这些订阅者。
先抽象一个观察者:
public interface Observer{
public void update(String message);
}
然后定义一个具体的观察者实现观察者接口,这个接口实现了更新的方法:
public class VXUser implements Observer{
private String vxName;
public VXUser(String vxName){
this.vxName = vxName;
}
@Override
public void update(String message){
System.out.println(name + ",您专注的" + message);
}
}
有了观察者之后,我们再定义发布者,也就是微信公众号:
public interface Subject{
//增加订阅者
public void registerObserver(Observer observer);
//删除订阅者
public void removeObserver(Observer observer);
//更新消息时通知订阅者
public void notify(String message);
}
同上我们再定义一个具体的发布者类——微信公共号,内部维护了一个订阅该公众号的 list,并实现了接口中的方法:
public class VXSubscriptionSubject implements Subject{
//存储订阅公众号的微信用户
private List<Observer> vxUserList = new ArrayList<>();
@Override
public void registerObserver(Observer observer){
vxUserList.add(observer);
}
@Override
public void removeObserver(Observer observer){
vxUserList.remove(observer);
}
@Override
public void notify(String message){
for(Observer ob : vxUserList){
observer.update(message);
}
}
}
以上就完成了整个观察者模式的基本功能,我们可以写一个客户端程序试一下:
public class Client{
public static void main(String[] args){
VXSubscriptionSubject vxSub = new VXSubscriptionSubject();
//创建一个微信用户
VXUser user1 = new VXUser("user1");
VXUser user2 = new VXUser("user2");
VXUser user3 = new VXUser("user3");
//订阅整个公众号
vxSub.registerObserver(user1);
vxSub.registerObserver(user2);
vxSub.registerObserver(user3);
//公共号发布新消息
vxSub.notify("NBA专栏更新啦");
}
}
运行后输出的结果如下:
user1,您关注的NBA专栏更新啦
user2,您关注的NBA专栏更新啦
user3,您关注的NBA专栏更新啦
综上我们可以看到,观察者模式的实现接触了耦合,让双方都依赖于抽象,从而使得各自的变换都不会影响到另一方。但是在应用的时候需要考虑一下开发效率和运行效率的问题,程序中包含一个发布者,多个观察者,开发、调试等内容会比较复杂,而且在 Java 中消息的通知一般是顺序执行,那么一个观察者卡顿了,会影响整体的执行效率,在这种情况下需要使用异步实现。