观察者模式

观察者模式

定义:描述了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动做出相应的反应。

场景

  1. 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变
  2. 当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合

观察者模式结构

观察者模式包含4种角色:

  1. 主题:一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法
  2. 观察者:一个接口,该接口规定了具体观察者用来更新数据的方法
  3. 具体主题:实现主题接口类的一个实例。该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者
  4. 具体观察者:实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者

观察者模式结构的类图如下所示:

进一步了解观察者模式

优点:

  1. 具体主题和具体观察者是松耦合关系。由于主题接口仅仅依赖于观察者接口,因此具体主题只是知道它的观察者是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类
  2. 观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知

缺点:

  1. 如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  2. 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。

代码实例

代码演示的内容:一个大学毕业生希望能及时知道“求职中心”最新的职业需求信息,我们利用观察者模式来模拟这个事件。

主题(Subject):

1
2
3
4
5
public interface Subject {
public void addObserver(Observer o);
public void deleteObserver(Observer o);
public void notifyObservers();
}

观察者(Observer):

1
2
3
4
public interface Observer {
//对应类图的update方法
public void hearTelephone(String heardMess);
}

具体主题(ConcreteSubject):

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
import java.util.ArrayList;
public class SeekJobCenter implements Subject{
String mess;
boolean changed;
ArrayList<Observer> personList; //存放观察者引用的数组线性表
SeekJobCenter(){
personList = new ArrayList<Observer>();
mess = "";
changed = false;
}
public void addObserver(Observer o){
if(!(personList.contains(o))){
personList.add(o); //把观察者的引用添加到数组线性表
}
}
public void deleteObserver(Observer o){
if(personList.contains(o)){
personList.remove(o); //把观察者的引用移除数组线性表
}
}
public void notifyObservers(){
if(changed){ //通知所有的观察者
for(int i = 0;i < personList.size();i++){
Observer observer = personList.get(i);
observer.hearTelephone(mess); //让所有的观察者接听电话
}
}
}
public void getNewMess(String str){ //判断信息是否是新发布的
if(str.equals(mess)){
changed = false;
}
else{
mess = str;
changed = true;
}
}
}

具体观察者(ConcreteObserver):

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
import java.io.*;
public class UniversityStudent implements Observer {
Subject subject;
File myFile;
UniversityStudent(Subject subject,String fileName){
this.subject = subject;
subject.addObserver(this); //使当前实例成为subject所使用的具体主题的观察者
myFile = new File(fileName);
}
public void hearTelephone(String heardMess) {
try{
RandomAccessFile out1 = new RandomAccessFile(myFile,"rw");
out1.seek(out1.length());
byte[] b = heardMess.getBytes();
out1.write(b); //更新文件中的内容
System.out.print("我是一个大学生,");
System.out.println("我向文件"+myFile.getName()+"写入如下内容:");
System.out.println(heardMess);
}
catch(IOException exp){
System.out.println(exp.toString());
}
}
}

具体调用实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Application {
public static void main(String[] args) {
SeekJobCenter center = new SeekJobCenter(); //具体主题center
UniversityStudent qiuqiu = new UniversityStudent(center,"A.txt"); //具体观察者qiuqiu
center.getNewMess("阿里巴巴需要10个Java程序员。"); //具体主题给出新信息
center.notifyObservers(); //具体主题通知信息
center.getNewMess("网易需要8个动画设计师。");
center.notifyObservers();
center.getNewMess("网易需要9个前端。");
center.notifyObservers();
center.getNewMess("网易需要9个前端。"); //信息不是新的
center.notifyObservers(); //观察者不会执行更新操作
}
}

输出:

我是一个大学生,我向文件A.txt写入如下内容:
阿里巴巴需要10个Java程序员。
我是一个大学生,我向文件A.txt写入如下内容:
网易需要8个动画设计师。
我是一个大学生,我向文件A.txt写入如下内容:
网易需要9个前端。

0%