文章目录
- 1、观察者模式
- 2、案例:模拟微信公众号
- 3、总结
- 4、在源码中的实际应用
1、观察者模式
发布订阅模式,一对多,让多个观察者对象同时监听某一个主题对象。主题对象状态发生变化时,通知所有的观察者对象,让它们自动更新自己。角色:
- 抽象主题:把所有观察者对象保存在一个集合中(可在具体主题中),并提供新增、删除观察者的方法
- 具体主题:具体主题内部状态变化时,给所有注册过的观察者发通知
- 抽象观察者:定义一个更新接口,给主题类调用
- 具体观察者:实现抽象观察者,重写主题对象变更时,更新自身状态的逻辑
2、案例:模拟微信公众号
公众号更新文章,通知所有微信用户的关注者。
定义抽象主题类,里面包含添加、删除观察者对象的方法:
public interface Subject {
//增加订阅者对象
public void attach(Observer observer);
//删除订阅者对象
public void detach(Observer observer);
//通知订阅者更新消息
public void notify(String message);
}
定义具体的主题类:微信公众号,里面定义一个存放所有观察者对象的集合:
public class SubscriptionSubject implements Subject {
//储存订阅公众号的微信用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message); //调用观察者的update方法,里面走每个具体观察者自己的更新逻辑
}
}
}
定义抽象观察者类,里面定义一个更新的方法:
public interface Observer {
void update(String message); //主题类对象发生改变时,通知观察者对象去更新,给主题类去调用的
}
定义具体观察者类,实现更新的方法,写自己的更新逻辑:
public class WeixinUser implements Observer {
// 微信用户名
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
//按需写主题对象变化时,自己的更新逻辑
System.out.println(name + "-" + message);
}
}
测试类:
public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建微信用户
WeixinUser user1=new WeixinUser("甲");
WeixinUser user2=new WeixinUser("乙");
WeixinUser user3=new WeixinUser("丙");
//订阅公众号,即把观察者对象加入到主题类的观察者集合中
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);
//公众号更新发出消息给订阅的微信用户
mSubscriptionSubject.notify("主题对象更新了");
}
}
运行:
甲-主题对象更新了
乙-主题对象更新了
丙-主题对象更新了
3、总结
优点:
- 降低了主题和观察者的耦合,没有完全解耦合,但面向抽象。当新增主题时或者观察者,也满足开闭
- 实现了广播机制
缺点:
- 观察者很多时,集合中排后面的观察者收到消息的耗时较长
适用场景:
- 对象之间存在一对多关系 + 一个对象状态的改变会影响到其他对象
4、在源码中的实际应用
JDK中, java.util.Observable
是抽象主题类, java.util.Observer
是抽象观察者类。写它们的子类,就可以实现观察者模式。
抽象主题类,里面有一个Vector集合,并在无参构造中完成了初始化。相关方法:
- addObserver方法:添加观察者
- notifyObservers方法:调用观察者对象的update方法,当内部标志为true时更新,且clearChanged方法将标志位复原
- setChange方法:改变内部标志,change为true时,通知观察者
举例:用JDK的抽象主题和抽象观察者,写子类模拟警察抓小偷。小偷被观察的,是具体主题,注意代表着主题对象发生变化的steal方法,里面调用父类的setChange方法改变状态位,并调notifyObservers方法通知所有观察者:
public class Thief extends Observable {
private String name;
public Thief(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//代表着主题发生了变化,需要通知,那就改变标志位
public void steal() {
System.out.println("小偷:我偷东西了,有没有人来抓我!!!");
super.setChanged(); //changed = true !!
super.notifyObservers(); //!!
}
}
警察是一个观察者,所以需要让其实现Observer接口,重写update方法
public class Policemen implements Observer {
private String name;
public Policemen(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了");
}
}
测试:
public class Client {
public static void main(String[] args) {
//创建主题对象
Thief t = new Thief("隔壁老王");
//创建观察者对象
Policemen p = new Policemen("小李");
//让警察盯着小偷(观察者加入主题的观察者集合中)
t.addObserver(p);
//小偷偷东西(主题对象发生变化)
t.steal();
}
}