当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。
观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
前言
意图:定义对象的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新.
应用实例:
- 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。
Android开发的过程,监听 - 当应用中的一些对象必须观察其他对象时, 可使用该模式。 但仅能在有限时间内或特定情况下使用。
订阅列表是动态的, 因此订阅者可随时加入或离开该列表。
- 当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。
优点
- 开闭原则。 你无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类).
- 你可以在运行时建立对象之间的联系。
缺点
仔细检查你的业务逻辑, 试着将其拆分为两个部分: 独立于其他代码的核心功能将作为发布者; 其他代码则将转化为一组订阅类。
声明订阅者接口。 该接口至少应声明一个 update方法。
声明发布者接口并定义一些接口来在列表中添加和删除订阅对象。 记住发布者必须仅通过订阅者接口与它们进行交互。
确定存放实际订阅列表的位置并实现订阅方法。 通常所有类型的发布者代码看上去都一样, 因此将列表放置在直接扩展自发布者接口的抽象类中是显而易见的。 具体发布者会扩展该类从而继承所有的订阅行为。
创建具体发布者类。 每次发布者发生了重要事件时都必须通知所有的订阅者。
在具体订阅者类中实现通知更新的方法。 绝大部分订阅者需要一些与事件相关的上下文数据。 这些数据可作为通知方法的参数来传递。
但还有另一种选择。 订阅者接收到通知后直接从通知中获取所有数据。 在这种情况下, 发布者必须通过更新方法将自身传递出去。 另一种不太灵活的方式是通过构造函数将发布者与订阅者永久性地连接起来。客户端必须生成所需的全部订阅者, 并在相应的发布者处完成注册工作。
示例
Observer.java
1
2
3
4
5package com.edu.tju.GOF.Observer;
public interface Observer {
public abstract void update(NumberGenerator generator);
}NumberGenerator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package com.edu.tju.GOF.Observer;
import java.util.ArrayList;
import java.util.Iterator;
public abstract class NumberGenerator {
private ArrayList<Observer> observers = new ArrayList<Observer>(); // 保存Observer们
public void addObserver(Observer observer) { // 注册Observer
observers.add(observer);
}
public void deleteObserver(Observer observer) { // 删除Observer
observers.remove(observer);
}
public void notifyObservers() { // 向Observer发送通知
Iterator it = observers.iterator();
while (it.hasNext()) {
Observer o = (Observer)it.next();
o.update(this);
}
}
public abstract int getNumber(); // 获取数值
public abstract void execute(); // 生成数值
}RandomNumberGenerator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.edu.tju.GOF.Observer;
import java.util.Random;
public class RandomNumberGenerator extends NumberGenerator {
private Random random = new Random(); // 随机数生成器
private int number; // 当前数值
public int getNumber() { // 获取当前数值
return number;
}
public void execute() {
for (int i = 0; i < 20; i++) {
number = random.nextInt(50);
notifyObservers();
}
}
}DigitObserver.java
1
2
3
4
5
6
7
8
9
10
11package com.edu.tju.GOF.Observer;
public class DigitObserver implements Observer {
public void update(NumberGenerator generator) {
System.out.println("DigitObserver:" + generator.getNumber());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}GraphObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.edu.tju.GOF.Observer;
public class GraphObserver implements Observer {
public void update(NumberGenerator generator) {
System.out.print("GraphObserver:");
int count = generator.getNumber();
for (int i = 0; i < count; i++) {
System.out.print("*");
}
System.out.println("");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}Main.java
1
2
3
4
5
6
7
8
9
10
11
12package com.edu.tju.GOF.Observer;
public class Main {
public static void main(String[] args) {
NumberGenerator generator = new RandomNumberGenerator();
Observer observer1 = new DigitObserver();
Observer observer2 = new GraphObserver();
generator.addObserver(observer1);
generator.addObserver(observer2);
generator.execute();
}
}
角色
Subject(观察对象,抽象类)
Subject角色表示观察对象,Subject角色定义了注册观察者和删除观察者的方法.此外,它还声明了”获取现在的状态”的方法.ConcreteSuject(具体的观察对象)
具体的被观察对象,当自身状态发生变化后,它会通知所有已经注册的Observer角色.Observer(观察者)
Observer角色负责接受来自Subject角色的状态变化的通知.为此,它声明了update方法.在示例程序中,由Observer接口扮演此角色.ConcreteObserver(具体的观察者)
表示具体的Observer.当它的update方法被调用后,会去获取要观察的对象的最新状态.Observer模式的类图
可替换性
- 利用抽象类和接口从具体类中抽象方法
- 在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口
练习
这个例子中就将发布者与订阅者永久性地连接起来。
Subject.java
这里为了简单,没有使用ConcreteSuject.如果需要课自行修改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
33package com.edu.tju.GOF.Observer2;
import java.util.ArrayList;
import java.util.Iterator;
public class Subject {
private ArrayList<Observer> observers = new ArrayList<Observer>();
private int state;
public void addObserver(Observer observer) {
observers.add(observer);
}
public void deleteObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
// Iterator<Observer> it = observers.iterator();
// while(it.hasNext()) {
// Observer ob = it.next();
// ob.update();
// }
for(Observer observer: observers) {
observer.update();
}
}
public int getState() {
return state;
}
public void setState(int state) {
this.state =state;
notifyObservers();
}
}Observer.java
1
2
3
4
5
6package com.edu.tju.GOF.Observer2;
public abstract class Observer {
protected Subject subject;
public abstract void update();
}HexObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.edu.tju.GOF.Observer2;
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.addObserver(this);
}
public void update() {
System.out.println("Binary String: " + Integer.toBinaryString(subject.getState()));
}
}OctObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13package com.edu.tju.GOF.Observer2;
public class OctObserver extends Observer {
public OctObserver(Subject subject){
this.subject = subject;
this.subject.addObserver(this);
}
public void update() {
System.out.println("Octal String: " + Integer.toOctalString(subject.getState()));
}
}HexObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13package com.edu.tju.GOF.Observer2;
public class HexObserver extends Observer {
public HexObserver(Subject subject){
this.subject = subject;
this.subject.addObserver(this);
}
public void update() {
System.out.println("Hex String: " + Integer.toHexString(subject.getState()).toUpperCase());
}
}Main.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.edu.tju.GOF.Observer2;
public class Main {
public static void main(String[] args) {
Subject subject = new Subject();
new HexObserver(subject);
new OctObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}