设计模式-观察者模式

观察者模式(Observer) : 当存在一对多的关系, 发生了改变, 与其有关系的 也要执行相关的动作. 也叫做发布订阅模式, 属于对象行为模式.

设计模式-概述

结构

观察者模式有如下主要角色:

  1. 抽象主题(Subject) : 它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  2. 具体主题(ConcreteSubject) : 实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  3. 抽象观察者(Observer) : 包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  4. 具体观察者(ConcreteObserver) : 实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

observer-1.png

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
@startuml observer-1
!include common.cuml

Class(AbstractSubject,"抽象主题") << abstract >> {
- observers: List<Observer>
+ add(Observer observer) : void
+ remove(Observer observer) : void
+ notifyAllObserver() : void abstract
}

Class(ConcreteSubject,"具体主题") {
+ notifyAllObserver() : void
}

note as N1
public void notifyAllObserver() {
for (Observer observer : Observers) {
observer.response();
}
}
end note

Interface(Observer,"抽象观察者") {
+ response() : void
}

Class(ConcreteObserverA,"具体主题A") {
+ response() : void
}

Class(ConcreteObserverB,"具体主题B") {
+ response() : void
}

N1 -- ConcreteSubject
AbstractSubject <|-- ConcreteSubject
Observer --o AbstractSubject
ConcreteObserverA ..|> Observer
ConcreteObserverB ..|> Observer

@enduml

代码实现

  1. 创建一个主题
  2. 创建n个观察者, 对这个主题进行绑定.
  3. 这个主题可能在某时刻发生改变, 发生改变后可以调用 notifyObservers() 方法.
  4. notifyObservers() 方法内部封装了通知各个观察者的逻辑.
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
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.yuda.test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {

public static void main(String[] args) {
Subject subject = new ConcreteSubject();
// 维护绑定关系
Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();
Observer observerN = new ConcreteObserverN();
subject.add(observerA, observerB, observerN);
// 触发
subject.notifyObservers();
}
}

abstract class Subject {
protected List<Observer> observerList = new ArrayList<>();

public void add(Observer... observer) {
observerList.addAll(Arrays.asList(observer));
}

public void remove(Observer observer) {
observerList.remove(observer);
}

public abstract void notifyObservers();
}

class ConcreteSubject extends Subject {

@Override
public void notifyObservers() {
for (Observer observer : observerList) {
observer.response();
}
}
}

interface Observer {
void response();
}

class ConcreteObserverA implements Observer {

@Override
public void response() {
System.out.println("ConcreteObserverA.response");
}
}

class ConcreteObserverB implements Observer {

@Override
public void response() {
System.out.println("ConcreteObserverB.response");
}
}

class ConcreteObserverN implements Observer {

@Override
public void response() {
System.out.println("ConcreteObserverN.response");
}
}

使用场景

  1. 各个观察者(Observer)需要是独立的, 没有先后顺序.
  2. 观察者个数可控, 触发观察者是通过循环来实现的, 如果太多循环起来特别慢.
  3. 观察者与主题之间不能存在循环引用, 否则出现主题变更=>触发观察者=>观察者触发主题变更=>…., 最终程序卡死.

具体应用

  1. jdk中的 java.util.Observablejava.util.Observer 定义了观察者模式, 需要用可以直接实现接口

  2. excel处理工具 poi 中的 DataFormatter 实现了 java.util.Observer, 并且定义了一个内部类 LocaleChangeObservable 在适当的地方调用 checkForLocaleChange() 方法来触发 观察者的update().

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
    </dependency>

    observer-2.png

    observer-3.png

    observer-4.png

    observer-5.png

    observer-6.png

  3. Spring中的事件驱动模型, 主要类有: ApplicationEvent, ApplicationListener, ApplicationContext, ApplicationEventMulticaster.

    • ApplicationEvent继承自jdk的EventObject,所有的事件都需要继承ApplicationEvent,并且通过source得到事件源.该类的实现类ApplicationContextEvent表示ApplicaitonContext的容器事件.
    • ApplicationListener继承自jdk的EventListener,所有的监听器都要实现这个接口
    • 事件机制的实现需要三个部分,事件源,事件,事件监听器,在上面介绍的ApplicationEvent就相当于事件,ApplicationListener相当于事件监听器,这里的事件源说的就是applicaitonContext.
    • 属于事件广播器,它的作用是把Applicationcontext发布的Event广播给所有的监听器.