观察者模式

实现观察者模式时要注意具体目标对象具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

定义

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新

实现

观察者模式通用类图

Subject被观察者,定义被观察者的实现职责,必须能够动态的增加、取消观察者,一般是抽象类或者实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class Subject {
//定义一个观察者数组
private Vector<Observer> obsVector = new Vector<Observer>();
//增加一个观察者
public void addObserver(Observer o){
this.obsVector.add(o);
}
//删除一个观察者
public void delObserver(Observer o){
this.obsVector.remove(o);
}
//通知所有观察者
public void notifyObservers(){
for(Observer o:this.obsVector){
o.update();
}
}
}

Observer观察者,观察者接收到消息后,对消息进行处理。

1
2
3
public interface Observer {
void update();
}

ConcreteSubject具体的被观察者,定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

1
2
3
4
5
6
7
public class ConcreteSubject extends Subject {
//具体的业务
public void doSomething(){
System.out.println("do something");
super.notifyObservers();
}
}

ConcreteObserver具体的观察者,每个观察者接收到消息后的处理逻辑是不一样的。

1
2
3
4
5
6
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("接收到信息, 并进行处理! ");
}
}

客户端使用

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
//创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer obs= new ConcreteObserver();
//观察者观察被观察者
subject.addObserver(obs);
//观察者开始活动了
subject.doSomething();
}

优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
  • 目标与观察者之间建立了一套触发机制。形成了一个触发链。 观察者模式可以完美地实现这里的链条形式。

缺点

  • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用
  • 观察者对象很多时,开发和调试就会比较复杂,通知的发布会花费很多时间,影响程序的效率,且一个观察者卡壳,会影响整体的执行效率;在这种情况下,一般考虑采用异步的方式。

应用

在软件系统中,当系统一方行为依赖另一方行为的变动时,可使用观察者模式松耦合联动双方,使得一方的变动可以通知到感兴趣的另一方对象,从而让另一方对象对此做出响应。

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  • 实现类似广播机制的功能,不需要知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播。
  • 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

注意

广播链的问题,一个观察者可以有双重身份,既是观察者,也是被观察者,链一旦建立,逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。

它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。

异步处理问题,被观察者发生动作,观察者要做出回应,如果观察者比较多,而且处理时间比较长,就用异步处理,异步处理就要考虑线程安全和队列的问题。

扩展

Java提供了java.util.Observable类和java.util.Observer接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。

Observable类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Observable {
// 内部标志位,注明目标对象发生了变化,为true时,notifyObservers()才会通知观察者
private boolean changed = false;
private Vector<Observer> obs;
public Observable() {
obs = new Vector<>();
}

public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}

public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}

public void notifyObservers() {
notifyObservers(null);
}

public void notifyObservers(Object arg) {
Object[] arrLocal;

synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}

for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}

public synchronized void deleteObservers() {
obs.removeAllElements();
}

protected synchronized void setChanged() {
changed = true;
}

protected synchronized void clearChanged() {
changed = false;
}

public synchronized boolean hasChanged() {
return changed;
}

public synchronized int countObservers() {
return obs.size();
}
}

Observer接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用void update(Observable o,Object arg)方法,进行相应的工作。

1
2
3
public interface Observer {
void update(Observable o, Object arg);
}

利用Observable类和Observer接口实现观察者模式实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteSubject extends Observable {
public void doSomething(){
System.out.println("do something");
// 设置内部标志位,注明数据发生变化
super.setChanged();
super.notifyObservers("msg");
}
}

public class ConcreteObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("接收到信息:" + arg);
}
}

public static void main(String[] args) {
//创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer obs= new ConcreteObserver();
//观察者观察被观察者
subject.addObserver(obs);
//观察者开始活动了
subject.doSomething();
}