1、接口回调
在学习观察者模式之前,我们先了解一下接口回调的概念。两者的原理有些类似,理解了接口回调就非常容易理解观察者模式。
所谓接口回调一般应用的场合是:你不知道这个方法什么时候回返回,但是你希望在该方法结束的时候拿到方法执行的结果。常见的,比如一个方法内部开启了线程,而我们希望在线程执行结束的时候拿到线程的执行结果。
在下面的例子中,方法 cal()
定义了一个局部变量 i,随后开启了一个线程,并在线程执行的时候修改了 i 的值。我们希望在线程执行完毕的时候达到 i 的执行结果。
public int cal() {
int i;
new Thread(new Runnable() {
Thread.sleep(3000); // throw ... ignore it
i++;
}).start();
return i;
}
使用 return 肯定不行,因为方法结束时,线程可能还没有结束,那么 return 返回的结果是无法被预料到的,可能是线程执行完毕之后的,也可能是没有被线程修改就返回了的。
我们可以使用接口回调解决这个问题。我们可以在调用该方法的时候传入一个接口 Callback 的实例。在线程中执行完所有的逻辑之后,我们使用该接口的 call()
方法将 i 回调出来:
public void cal(Callback callback) {
int i;
new Thread(new Runnable() {
Thread.sleep(3000); // throw ... ignore it
i++;
if (callback != null) {
callback.call(i); // 1
}
}).start();
}
当然,接口回调更像是将我的 call()
方法注入到了上述代码中的 1 处。这近似于所谓的函数编程的概念,就是将接口作为一个函数注入到了方法中。
接口回调有非常丰富的应用场景,典型的是一些异步的场景,比如 “监听” 一个按钮的执行结果等等。
2、观察者模式
好了,了解了上面的接口回调之后,我们来看下观察者模式。首先,我们来了解一下观察者设计模式中的一些概念。
如果使用 1 中的的例子来做类比的话,那么 i 就是我们的主题(Subject),我们进行接口回调的类(将实现的 Callback 传入到 cal 方法时所处的类)叫做观察者(Subscriber)。
上面的是观察者模式的UML模型。这里的 Subject
接口就是主题的接口,而 ConcreteSubkect
是它的具体实现类,即具体的 主题。Observer
接口是观察者的接口,ConcreteObserver
是具体的观察者。一个主题往往会通过列表来维护一系列的观察者,然后当主题发生变化的时候会便利这个列表来通知所有的观察者。所以,这里的 Subject
接口定义了三个方法,从上到下依次用于向主题中添加观察者,从主题中移除观察者以及通知所有的观察者主题的更新。
下面我们给出一份最简单的观察者设计模式的代码:
1.首先是主题的定义类:
public class ConcreteSubject implements Subject {
// 通过队列维护观察者列表
private List<Observer> observers = new LinkedList<>();
// 注册一个观察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
// 移除一个观察者
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}
// 通知所有观察者主题的更新
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.method();
}
}
}
2.接下来是观察者的定义类:
public class ConcreteObserver implements Observer {
// 该观察者订阅的主题
private Subject subject;
public ConcreteObserver(Subject subject) {
this.subject = subject;
// 将当前观察者添加到主题订阅列表中
subject.registerObserver(this);
}
// 当主题发生变化的时候,主题会遍历观察者列表并通过调用该方法来通知观察者
@Override
public void method() {
// ...
}
}
上面就是观察者的基本的实现方式,这里的实现逻辑比较简单,但当你学习更加复杂的观察者设计的之前理解它是很有必要的。
3、总结
观察者与被观察者之间是属于轻度的关联关系,并且是抽象耦合的,这样,对于两者来说都比较容易进行扩展。
观察者模式是一种常用的触发机制,它形成一条触发链,依次对各个观察者的方法进行处理。但同时,这也算是观察者模式一个缺点,由于是链式触发,当观察者比较多的时候,性能问题是比较令人担忧的。并且,在链式结构中,比较容易出现循环引用的错误,造成系统假死。并且因为观察者列表中维护了一份观察者的引用,当它们没有被及时地释放的话,可能会引起内存泄漏。