观察者模式的定义
观察者( Observer )模式又名发布 - 订阅( Publish/Subscribe )模式。
从字面意思上看,观察者,就是一个对象观察另一个对象是否发生了变化,从这个角度来理解的话,很容易会误认为观察者一直保持观察这个状态,其实不然。
实际上,观察者模式,主要包含两种角色:被观察者和观察者。一个比较现实的例子是这样的,一群老鼠在厨房偷东西吃,突然,猫大叫一声,所有的老鼠立刻四处逃窜。在这个实例里,老鼠是观察者,而猫的是被观察者。很明显,作为观察者,老鼠并不是一味的在倾听是否有猫的叫声,反而是猫的一声大叫“通知”(吓跑)了老鼠。所以,从概念的层次来看,观察者不是一味的观察别观察者是否发生了变化,而是被观察者在发生变化的时候主动通知观察者执行相应的动作。就像例子里的猫(被观察者)大叫一声,就是要告知老鼠(观察者)离开。
所以,就此引出观察者模式的定义: 观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监察一个主题对象。这样一个主题对象在状态上的变化能够通知所有的依赖于此对象的那些观察者对象,使这些观察者对象能够自动更新。
观察者模式的实现原理
首先,观察者将自身注册到被观察者的管理列表里,以方便被观察者发出通知,这一过程,称之为“订阅”。接着,当被观察者的属性发生改变时,通知管理列表里的所有观察者(也就是订阅了该服务的对象),该过程称为“发布”。最后,观察者在接到通知后,执行相关的行为。
参考代码如下:
1. 被观察者 类
import java.util.ArrayList;
import java.util.List;
public class Subject {
/** 变化是否发生的标志 */
private boolean isChanged = false;
/** 监视的数据 */
private int data;
/** 观察者集合 */
private List<MyObserver> obsvrList = new ArrayList<MyObserver>();
/**
* 注册观察者
* @param observer
*/
public void registerObserver(MyObserver observer) {
if(observer == null) {
throw new NullPointerException();
}
if(!obsvrList.contains(observer)) {
obsvrList.add(observer);
}
}
/**
* 去除观察者
* @param observer
*/
public void removeObserver(MyObserver observer) {
obsvrList.remove(observer);
}
/**
* 通知所有观察者
*/
private void notifyObserver() {
if(!isChanged) return;
//通知所有的observer
for (MyObserver obsvr : obsvrList) {
obsvr.update();
}
clearChanged();
}
private void setChanged() {
isChanged = true;
}
private void clearChanged() {
isChanged = false;
}
public void setData(int data) {
//如果数据未发生变更,则不执行任何操作
if(this.data == data) return;
this.data = data;
setChanged();
notifyObserver();
}
}
2. 观察者类
public class Observer {
public void update() {
System.out.println("被观察者发生改变!");
}
}
3.测试类
public class Test {
public void main(String[] args) {
Subject s = new Subject();
Observer obsvr = new Observer();
s.registerObserver(obsvr);
s.setData(6);
}
}
在实际的应用中,为了更好的降低观察者和被观察者之间的耦合度,我们会定义四个角色:
抽象主题(Subject)角色 :主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;主题角色又叫做抽象被观察者(Observable)角色;
抽象观察者(Observer)角色 :为所有的具体观察者定义一个接口,在得到通知时更新自己;
具体主题(ConcreteSubject)角色 :从抽象主题继承而来,保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;具体主题角色又叫作具体被观察者角色;
具体观察者(ConcreteObserver)角色 :从抽象观察者继承而来,保存一个指向具体主题对象的引用,是最终发出响应的对象。
四种角色的类图如下:
这样以来,具体的观察者无须知道被观察的对象是谁,长什么样,而具体的被观察者也不用知道都有那些观察者关注他。这在很大的程度上降低了代码之间的耦合度。那么,这样设计是怎么有效降低耦合度的呢?我们接下来看看Java对观察者模式的支持。
Java对观察者模式的支持
Java为开发者提供了一个类和一个接口,分别是Observable和Observer。
Observable就是我们之前讨论的抽象主题类,Java在这里做了一个很聪明的处理,这个类包含两个属性,分别是(changed, 布尔型, 标志这变化是否发生)和obs(集合类型,用来存储观察者的引用)。
并且提供了一些操作这两个属性的方法。
这样,对于使用者(具体主题类)来说,只要调用抽象主题类的方法,就可以了。具体主题类的示例代码如下:
public class Concrete extends Observable {
/** 被观察的数据 */
private int data;
public void setTemperature(int data) {
if(this.data == data) {
return;
}
this.data = data;
setChanged(); //调用父类的方法,设置变化点
notifyObservers(temperature); //调用父类的方法,通知观察者
}
}
观察者模式使用过程中应该注意的问题
单一职责原则
观察者模式的优缺点