1. 观察者模式定义
观察者模式:又称发布订阅模式,当一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。【引自百度百科】
这里的目标物件,就是被观察者,当它的状态发生变更时,需要去通知所有观察它的观察者。
根据定义大体就能知道,被观察者需要将一个个观察者管理起来,否则它怎么知道通知谁呢?这才是核心。
2. 类图
自己感觉这个模式还是比较简单的,直接上类图吧:
该模式有四种角色:
Subject角色:被观察的对象,它实现了添加观察者和删除观察者的方法。另外,它还有一个notify方法用于通知所有的观察者。
ConcreteSubject角色:具体的观察者,这里视具体情况而定,感觉这个角色不是必须的。
Observer角色:它是一个接口,定义了update 方法,用于接受来着Subject角色的状态变化通知。
ConcreteObserver角色:表示具体的Observer,当它的update方法被调用后,表示已经被通知到,然后就可以在这个时机去处理变化。
3. 例子
虽然简单,但还是举个小例子吧:
假如有很多观察者,去观察一个会产生随机数字的对象,当产生随机数字的对象产生随机数时,就会通知对应的观察者,观察者将产生的数字打印出来。
观察者有两类,一种观察者直接把数字显示,另一种观察者以简单的图形显示数值。
首先定义NumberGenerator
类,用于产生数值:
/**
* the Observable
*/
public abstract class NumberGenerator {
//核心:需要将观察者管理起来
private ArrayList<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
//核心:当自己状态变化时,需要通知所有的观察者
public void notifyObservers() {
Iterator<Observer> it = observers.iterator();
while(it.hasNext()) {
Observer o = it.next();
o.update(this);
}
}
public abstract int getNumber();
public abstract void execute();
}
再定义具体的被观察者类RandomNumberGenerator
,用于产生随机数字:
/**
* the concrete Observable
*/
public class RandomNumberGenerator extends NumberGenerator {
private int number;
Random random = new Random();
@Override
public int getNumber() {
return number;
}
@Override
public void execute() {
for(int i = 0; i < 20; i++) {
number = random.nextInt(100);
notifyObservers();
}
}
}
接下来定义观察者接口Observer
:
/**
* observer
*/
public interface Observer {
//NumberGenerator 相当于上下文,可以获取被观察者的一些信息
void update(NumberGenerator generator);
}
具体的观察者DigitObserver
,只是打印普通的数字:
public class DigitObserver implements Observer{
@Override
public void update(NumberGenerator generator) {
System.out.println("DigitObserver has been notified, the number is :" + generator.getNumber());
}
}
具体的观察者GraphicObserver
,以简单的图形展示数字:
/**
* concrete observer
*/
public class GraphicObserver implements Observer {
@Override
public void update(NumberGenerator generator) {
System.out.println("GraphicObserver has been notified, the number is :" + generator.getNumber());
int count = generator.getNumber();
for(int i =0; i < count; i++) {
System.out.print("*");
}
System.out.println();
}
}
真正的客户端类:
public class Main {
public static void main(String[] args) {
NumberGenerator generator = new RandomNumberGenerator();
Observer observer1 = new DigitObserver();
Observer observer2 = new GraphicObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
//被观察者获取数字并通知一堆被观察者
generator.execute();
}
}
4.总结
这里被观察者去通知的时候根本不知道通知的具体是谁,它只认识Observer 接口的实例。这里体现了良好的扩展性。
观察者并非主动的观察,而是被动的接受Subject角色的通知。
JDK中也定义了Observable
类和Observer
接口,但是Observable是个具体的类,java又是单继承,导致复用性不好。