观察者模式
GOF概括:定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。
“观察者模式” 可以引申出非常多的知识点:“发布-订阅者模式”、“消息队列”……你几乎能在任何项目见到观察者模式的应用。
观察者模式是非常重要的一个设计模式,几乎会伴随着程序员走过整个职业生涯。
简单地说,就是一个对象,做了某件事,另一个对象要接着做点啥?
往大了说,卫星拍了一张照,新闻上你可能看到,天气系统,也可能会用到(不同系统之间存在消息订阅);
往小了说,就比如你 update 一条数据,日志系统要记录你的操作(一个代码模块内部也会有这种需求)。
简单案例
推送场景
这种代码结构非常常见,基本是要求背诵的。
一下子就能想到的,就是各种点击事件,例如:xxxClickListener(Event e)。
import java.util.ArrayList;
import java.util.List;
interface Observer{
void onAction(String action);
}
class Subject{
// 观察者集合
List<Observer> list = new ArrayList<>();
public void run(){
// do sth.
// then
notifyObserver("ok");
}
public void notifyObserver(String action){
for(Observer observer: list){
observer.onAction(action);
}
}
}
拉取场景
拉取模式与推送模式的区别是:收到消息之后,可以根据需求,拉取不同的数据。
这种写法,缩小了消息体的大小,按需拉取数据,代码解耦程度也更高一些。
当然,这并没有改变 “推送模式” 的本质,就比如下面代码,把 getMsg1() 放到另一个类,代码还是标准的推送模式。
说实话,对于 “拉取模式” 这个词,我是有些怀疑的,像是老师为了为难学生,自己创造出来的名词。
首先,代码本身就有点不伦不类的;而且,介绍 “观察者模式” 的文章,根本看不到 “拉取模式” 的内容。
不管具体怎样,不排除面试可能有人会这么问你,很多人都是这么学的,所以还是要掌握一下。
有些文章会用 http 协议举例,客户端需要去拉取后台的数据,但是很明显,后台状态发生变化,前端根本不会收到通知,这不符合 “观察者模式” 的定义。
import java.util.ArrayList;
import java.util.List;
class Observer{
public void onAction(Subject action){
System.out.println(action.getMsg1());
}
}
class Subject{
List<Observer> list = new ArrayList<>();
public void run(){
// do sth.
notifyObserver();
}
public String getMsg1(){
return "message1";
}
public String getMsg2(){
return "message2";
}
public void notifyObserver(){
for(Observer observer: list){
observer.onAction(this);
}
}
}
JDK标准实现
JDK 的 java.util 包提供了标准接口,可能整个职业生涯都用不上,
当然,多少要了解一些,因为面试可能会被提问。
实在不想了解,可以这么解释:因为 java 是单继承,Observable 的设计注定不会太实用;
业务上,需求往往十分复杂,使用 Observable 这样一个不常用、又很复杂的类,可能会增加维护负担。
一些文章认为,JDK 的实现是拉取模式,因为 update() 第一个参数是 Observable,
但是实际上,一个观察者,可以侦听很多消息,Observable 本身就是必要的,需要用它确定消息来源。
import java.util.Observable;
import java.util.Observer;
class Obs implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println(o);
System.out.println(arg);
}
}
class Subject extends Observable{
public void run(){
// do sth.
super.setChanged();
super.notifyObservers("msg");
}
}
public class Test2 {
public static void main(String[] args) {
Obs obs = new Obs();
Subject subject = new Subject();
subject.addObserver(obs);
subject.run();
}
}