观察者模式定义了一个一对多的依赖关系,当被观察者(主题)更新时会自动更新观察者们。在观察者模式中,被观察者无需知道有哪些观察者在观察它,只是在发生变化的时候调用更新的方法即可;观察者无需知道具体观察了什么对象,只是在被调用更新方法时自动更新自己的信息即可。观察者模式最大的优点即在于此,通过松耦合达到了对象间信息的传递。无论是JDK源码还是各种框架应用都大量的使用了这个模式。
观察者模式流程:
创建主题接口,主题接口是所有被观察者要实现的接口,主题接口中含有:添加、删除、更新三个方法,用于维护观察者列表。
创建被观察者接口,被观察者接口中只有一个更新方法,所有的被观察者都需要继承这个接口,用于实现更新方法,这个更新方法也是被观察者会调用的列表中的观察者的方法。
实现主题接口创建被观察者,作为一个合格的被观察者必须实现主题接口,实现三个方法,并且在该类中要维护一个列表,该列表中存放所有的观察者。增添方法将参数观察者传入列表中,删除方法即删除列表中的观察者,更新方法即调用列表中所有的观察者的更新方法。
实现观察者接口创建观察者,该观察者具备更新方法以供被观察者调用。在构造器中传入被观察者参数,并且直接用该参数调用添加方法添加该观察者至被观察者的列表中。并且该观察者中存有被观察者类型的引用,将上面参数的引用留下,以便以后删除方便。
main方法中创建被观察者,再创建观察者,将被观察者当作参数传入观察者实例中。这样就建立起了两个对象间的关系,当被观察者被改变时,观察者自动更新。
观察者模式技巧:
推与拉
a) 观察者模式有推与拉两种情况。在上述的过程中,我们将被观察者整个对象作为参数传入了观察者中。这样有两个问题,第一个问题不安全,观察者可以通过这个引用改变被观察者;第二个问题信息太多,有的观察者只需要一部分信息即可,但是更新方法每次都会把固定的那些信息推过去,观察者们很无奈,只能照单收下,这种方法叫做推。
b) 第二种方法是拉。很好理解,拉相比较推即是让观察者自己获取感兴趣的信息,在拉的观察者模式中,我们在更新方法中不传参数,而在被观察中编写所有要传递的信息的get方法,让观察者们自己获取想要获得的信息。
c) 通常,我们采用推方法,推方法是主流方法。setChange方法
a) 在JDK中有对观察者模式的实现,因为JDK中也有很多源代码是用观察者模式来实现的。在JDK的观察者模式中,我们发现多了一个setChange方法。这个方法的作用是设置一个字段change的布尔值,在更新方法中读取change的值判断是否需要进行更新。因为有的时候虽然数据发生了变化,但是我们不希望进行推送更新,那么这时我们将setChange方法设置为false即可避免更新观察者。JDK自带观察者模式缺点
a) 继承问题。JDK自带的观察者模式是java.util.Observable类,这个类最大的问题在于是可以被继承的类而不是接口,这给我们的编程带来了很大的困扰,如果我们的类必须要继承另外一个类,那么意味着观察者模式必须要手动实现了。
b) 时间问题。观察者模式需要被观察者主动的去更新观察者,如果观察者很多,那么通知的时间也很可观。而且这种通知,有时我们不敢使用多线程处理,因为要确保所有的观察者都获得了更新。
c) 死锁问题。如果观察者与被观察者之间会发生循环调用则可能导致死锁问题,这种问题一般在于代码的编写问题,如果真的会发生类死锁问题,我们该考虑是不是真的应该使用观察者模式了。就算使用也要自己做额外的实现代码。
d) 不存在内存问题。网上普遍说如果被观察者没有正确的被回收,那么观察者们可能得不到回收。这种说法是错的。Java虚拟机HotSpot采用可达性分析回收垃圾,不是引用计数法,不会发生内存泄露。
package ObserversEx;
import java.util.*;
public class Subject {
private boolean changed;
private ArrayList<Observer> observers = new ArrayList<Observer>();
protected synchronized void setChange() {
changed = true;
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void changeObservers(String data) {
int size = 0;
Observer[] arrays = null;
synchronized (this) {
if (changed) {
clearChanged();
size = observers.size();
arrays = new Observer[size];
observers.toArray(arrays);
}
}
if (arrays != null) {
for (Observer observer : arrays) {
observer.update(this, data);
}
}
}
protected synchronized void clearChanged() {
// TODO Auto-generated method stub
changed = false;
}
}
package ObserversEx;
public class Observer {
public void update(Subject subject, String data) {
}
}
package ObserversEx;
public class ObserverConcrete extends Observer {
private String obserState;
public void update(Subject subject, String data) {
System.out.println("Message is " + data);
}
}
package ObserversEx;
public class SubjectConcrete extends Subject {
public synchronized void change(String state) {
setChange();
changeObservers(state);
}
}
package ObserversEx;
public class Main {
public static void main(String[] args) {
SubjectConcrete subject = new SubjectConcrete();
Observer observer = new ObserverConcrete();
subject.addObserver(observer);
subject.change("new state");
}
}