观察者模式(Observer Pattern)也叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式。这个模式的一个最重要的作用就是解耦。也就是将被观察者和观察者进行解耦,使得他们之间的依赖性更小,甚至做到毫无依赖。在观察者模式中它定义了一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
1. 观察者模式结构
一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。下面以一个简单的示意性实现为例,讨论观察者模式的结构。
观察者模式主要包含四种角色:
-
Subject:抽象主题,也就是上面的被观察者(Observable)角色。Subject把所有观察者对象的引用保存在一个集合里,并且能够动态的增加、取消观察者
-
Observer:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口
-
ConcreteSubject:具体主题,也就是具体的被观察者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知
-
ConcreteObserver:具体观察者,ConcreteObserver实现了抽象观察者所定义的更新接口,再被观察者通知改变是更新自身状态,以便使本身的状态与主题的状态相协调
2. 简单实现
-
新建抽象主题
代码块
Java
public abstract class Subject {
//用来保存注册的观察者对象
private List<Observer> list = new ArrayList<Observer>();
//注册观察者对象:observer
public void attach(Observer observer) {
list.add(observer);
System.out.println("Attached an observer");
}
//删除观察者对象
public void detach(Observer observer) {
list.remove(observer);
}
//通知所有注册的观察者对象
public void nodifyObservers(String newState) {
for (Observer observer : list) {
observer.update(newState);
}
}
}
-
新建具体主题
代码块
Java
public class ConcreteSubject extends Subject{
private String state;
public void change(String newState){
state = newState;
System.out.println("主题状态为:" + state);
// 状态发生改变,通知各个观察者
this.nodifyObservers(state);
}
}
-
新建抽象观察者
代码块
Java
public interface Observer {
// 更新状态
public void update(String state);
}
-
新建具体观察者
代码块
Java
public class ConcreteObserver implements Observer {
//观察者的状态
private String observerState;
public void update(String state) {
/**
* 更新观察者的状态,使其与目标的状态保持一致
*/
observerState = state;
System.out.println("状态为:" + observerState);
}
}
-
测试类
代码块
Java
public class ObserverPatternTest {
public static void main(String[] args) {
// 创建主题对象
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者对象
Observer observer = new ConcreteObserver();
// 将观察者对象登记到主题对象上
subject.attach(observer);
// 改变主题对象的状态
subject.change("new state");
}
}
输出结果:
3. Java对观察者模式的支持
在java.util库里面,提供了一个Observable类和一个Observer接口。
-
Observable类
被观察者类都是java.util.Observable类的子类。java.util.Observable提供公开的方法支持观察者对象,这些方法中有两个对Observable的子类非常重要:一个是setChanged(),另一个是notifyObservers()。第一方法setChanged()被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化。第二个是notifyObservers(),这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
-
Observer接口
这个接口只定义了一个方法,即update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。
通过Java的内置类改写上述demo:
-
新建具体主题类
代码块
Java
public class ObserverPatternTest {
public static void main(String[] args) {
// 创建主题对象
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者对象
ConcreteObserver observer = new ConcreteObserver();
// 将观察者对象登记到主题对象上
subject.addObserver(observer);
// 改变主题对象的状态
subject.change("new state");
}
}
-
新建具体观察者
代码块
Java
public class ConcreteSubject extends Observable {
private String state;
public String getState() {
return state;
}
public void change(String newState) {
setChanged();
state = newState;
System.out.println("主题状态为:" + state);
// 状态发生改变,通知各个观察者
notifyObservers();
}
}
重新运行测试类,可发现结果一致。
4. 优缺点
优点:
-
观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。
-
观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。
-
观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。
-
观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。
缺点:
-
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
-
如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃
-
虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的