Java设计模式——观察者模式

模式定义

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

模式示例

Java本身对观察者模式的实现有良好的支持,被观察者类只要继承java.util.Observable即可,观察者类只要实现java.util.Observer接口即可。下面写一个Demo来描述观察者模式,这个示例如下,一个新闻事件中心,可以能会发生各种事件,有几家报社在新闻事件中心发生事件后,会根据事件做相应的报道,这是典型的一对多关系。

  • 定义被观察者接口,新闻中心实现被观察者接口,支持事件通知、添加或移除报社(观察者)操作;
  • 定义观察者接口,报社要实现该接口,当事件发生时,可以对事件进行响应。
  • 定义客户端来演示

定义被观察者

1
2
3
4
5
6
7
8
9
10
/**
* 被观察者接口
*/
public interface Subject {
void registerObserver(Observer object);

void removeObserver(Observer object);

void notifyObservers();
}

定义观察者接口

1
2
3
4
5
6
/**
* 观察者接口
*/
public interface Observer {
void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews);
}

定义真正被观察者NewsData,实现Subject接口

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
/**
* 真正的被观察者
*/
public class NewsData implements Subject {
private String headlineNews;
private String sportsNews;
private String financeNews;
private String entertainmentNews;

private List observerArrayList;

public NewsData() {
observerArrayList = new ArrayList();
}

@Override
public void registerObserver(Observer object) {
observerArrayList.add(object);
}

@Override
public void removeObserver(Observer object) {
if (observerArrayList.contains(object)) {
observerArrayList.remove(object);
}
}

@Override
public void notifyObservers() {
for (int i = 0; i < observerArrayList.size(); i++) {
((Observer) observerArrayList.get(i)).update(headlineNews, sportsNews, financeNews, entertainmentNews);
}
}

public void updateNews() {
notifyObservers();
}

public String getHeadlineNews() {
return headlineNews;
}

public String getSportsNews() {
return sportsNews;
}

public void setFinanceNews(String financeNews) {
this.financeNews = financeNews;
}

public void setEntertainmentNews(String entertainmentNews) {
this.entertainmentNews = entertainmentNews;
}

public void setHeadlineNews(String headlineNews) {
this.headlineNews = headlineNews;
}

public void setSportsNews(String sportsNews) {
this.sportsNews = sportsNews;
}

public String getFinanceNews() {
return financeNews;
}

public String getEntertainmentNews() {
return entertainmentNews;
}
}

定义四家报社(真正的观察者)这四家报社对新闻的关注点不同,可以选择性的报道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 关注头条、体育和娱乐新闻,不关注财经新闻
*/
public class CccNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;
private String entertainmentNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
this.entertainmentNews = entertainmentNews;
display();
}

public void display() {
System.out.println("Ccc headlineNews: " + headlineNews);
System.out.println("Ccc sportsNews: " + sportsNews);
System.out.println("Ccc entertainmentNews: " + entertainmentNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 关注头条、体育和财经新闻,不关注娱乐新闻
*/
public class DnnNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;
private String financeNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
this.financeNews = financeNews;
display();
}

public void display() {
System.out.println("Dnn headlineNews: " + headlineNews);
System.out.println("Dnn sportsNews: " + sportsNews);
System.out.println("Dnn finaceNews: " + financeNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 只关注娱乐新闻
*/
public class GuavaDailyNewsDisplay implements Observer {
private String entertainmentNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.entertainmentNews = entertainmentNews;
display();
}

public void display() {
System.out.println("GuavaDaily entertainmentNews: " + entertainmentNews);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 关注体育和头条
*/
public class YahuNewsDisplay implements Observer {
private String headlineNews;
private String sportsNews;

@Override
public void update(String headlineNews, String sportsNews, String financeNews, String entertainmentNews) {
this.headlineNews = headlineNews;
this.sportsNews = sportsNews;
display();
}

public void display() {
System.out.println("Yahu headlineNews: " + headlineNews);
System.out.println("Yahu sportsNews: " + sportsNews);
}
}

定义客户端,进行新闻播报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class NewsStation {

public static void main(String[] args) {
NewsData newsData = new NewsData();
CccNewsDisplay cccNewsDisplay = new CccNewsDisplay();
DnnNewsDisplay dnnNewsDisplay = new DnnNewsDisplay();
GuavaDailyNewsDisplay guavaDailyNewsDisplay = new GuavaDailyNewsDisplay();
YahuNewsDisplay yahuNewsDisplay = new YahuNewsDisplay();

newsData.registerObserver(cccNewsDisplay);
newsData.registerObserver(dnnNewsDisplay);
newsData.registerObserver(guavaDailyNewsDisplay);
newsData.registerObserver(yahuNewsDisplay);

newsData.setHeadlineNews("'Big objects found' in AirAsia hunt");
newsData.setFinanceNews("US shares barely budge in 2015");
newsData.setEntertainmentNews("Bono guitar fears after bike crash");
newsData.setSportsNews("World Tennis Championship: Andy Murray beats Rafael Nadal");

newsData.updateNews();

}

}

输出结果

1
2
3
4
5
6
7
8
9
Ccc headlineNews: 'Big objects found' in AirAsia hunt
Ccc sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal
Ccc entertainmentNews: Bono guitar fears after bike crash
Dnn headlineNews: 'Big objects found' in AirAsia hunt
Dnn sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal
Dnn finaceNews: US shares barely budge in 2015
GuavaDaily entertainmentNews: Bono guitar fears after bike crash
Yahu headlineNews: 'Big objects found' in AirAsia hunt
Yahu sportsNews: World Tennis Championship: Andy Murray beats Rafael Nadal

模式总结

一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。观察者和被观察者是抽象耦合的,被观察者持有一个观察者列表,在自身发生变化时,遍历该列表逐一通知。

观察者模式主要解决,一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

优缺点

  • 优点
    1. 观察者和被观察者是抽象耦合的。
    2. 建立一套触发机制。
  • 缺点
    1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
    2. 可能产生循环依赖的问题,如观察者和被观察者间可能存在循环依赖的问题(EventBus使用时就会产生这种问题)
    3. 观察者模式只能告诉观察者被观察者发生了变化,但不能说明具体发生了什么变化。

使用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

注意事项

  1. JAVA 中已经有了对观察者模式的支持类。
  2. 避免循环引用。
  3. 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。