1.定义
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
2.解决问题
——订阅报纸
看起来订阅者是直接根有据打交道,但实际上,订阅者的订阅数据是被邮寄传递到报社,当报社出了报纸,报社按订阅信息交给邮局,邮局在代为发送到订阅者手里。在整个过程中,邮局起到一个中转的作用。
使用观察者模式来解决问题
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
|
public
class
Subject {
private
List<Observer> readers =
new
ArrayList<Observer>();
public
void
attach(Observer reader) {
readers.add(reader);
}
public
void
detach(Observer observer) {
readers.remove(observer);
}
protected
void
notifyObservers() {
for
(Observer reader : readers)
reader.update(
this
);
}
}
public
class
NewsPaper
extends
Subject {
private
String content;
public
String getContent() {
return
content;
}
public
void
setContent(String content) {
this
.content = content;
super
.notifyObservers();
}
}
public
interface
Observer {
public
void
update(Subject subject);
}
public
class
Reader
implements
Observer {
private
String name;
public
void
update(Subject subject) {
name = ((NewsPaper)subject).getContent();
System.out.println(name);
}
}
public
class
Client {
public
static
void
main(String[] args) {
// 创建一个报纸,作为目标
NewsPaper subject =
new
NewsPaper();
// 创建一个读者
Reader reader1 =
new
Reader();
Reader reader2 =
new
Reader();
// 注册订阅者
subject.attach(reader1);
subject.attach(reader2);
// 要出报纸啦
subject.setContent(
"New"
);
}
}
|
3.模式讲解
上述问题中订阅者最关心的问题时是何时能收到最新处的报纸,但要是报纸出版的时间不固定怎么办?进一步抽象这个问题:当一个对象的状态发生改变的时候,如果让依赖于它的所有对象得到通知,并进行相应的处理呢?
解决思路
这是一个典型一对多的对象关系,一个报纸对象,有很多个订阅者对象来订阅,当报纸出版的时候,也就是报纸对象改变的时候。观察者模式处理这种问题时,把这多个订阅者称为观察者:Observer,多个观察者观察的对象称为目标: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
|
public
class
Subject {
// 用来保存注册的观察者对象
private
List<Observer> observers =
new
ArrayList<Observer>();
// 注册观察者对象
public
void
attach(Observer observer) {
observers.add(observer);
}
// 删除观察者对象
public
void
detach(Observer observer) {
observers.remove(observer);
}
// 通知所有注册的观察者对象
protected
void
notifyObservers() {
for
(Observer observer : observers)
observer.update(
this
);
}
}
public
class
ConcreteSubject
extends
Subject {
private
String subjectState;
public
String getSubjectState() {
return
subjectState;
}
public
void
setSubjectState(String subjectState) {
this
.subjectState = subjectState;
// 状态改变了,通知各个观察者
this
.notifyObservers();
}
}
public
interface
Observer {
public
void
update(Subject subject);
}
public
class
ConcreteObserver
implements
Observer {
private
String observerState;
public
void
update(Subject subject) {
// 可能需要更新观察者状态,使其与目标的状态保持一致
observerState = ((ConcreteSubject) subject).getSubjectState();
}
}
|
应用范围
按照模式的定义,目标和观察者之间是典型的一对多关系。但如果观察者只有一个,也是可以的;同样,一个观察者也可以观察多个目标。
单向依赖
在观察者模式中,观察者和目标是单向依赖,只有观察者依赖于目标,而目标不能依赖与观察者。
观察者模式调用顺序示意图
在使用观察者模式时,分为两个阶段:
第一个阶段是准备阶段,即维护目标和观察者关系的阶段;
第二阶段是实际运行阶段。
推模式和拉模式
推模式:目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部或部分数据,相当于广播通信。
拉模式:目标对象在通知观察者的时候,只传递少量信息,如果观擦或者需要更具体的信息,由观察者主动到目标对象中获取,相当于观察者从目标对象中拉数据。
Java观察者模式
java.util包里有一个Observable类,还有一个接口Observer,其中定义了update方法,就是观察者接口。
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
|
public
class
NewsPaper
extends
java.util.Observable {
private
String content;
public
String getContent() {
return
content;
}
public
void
setContent(String content) {
this
.content = content;
// 注意在Java中Observer模式的时候,下边这句不可少
this
.setChanged();
// 然后主动通知,这里用的推模式
this
.notifyObservers(
this
.content);
}
}
public
class
Reader
implements
java.util.Observer {
private
String name;
public
void
update(Observable o, Object obj) {
// 这是采用推的方式
System.out.println(name + obj);
// 这是采用拉的方式
System.out.println(name + ((NewsPaper) o).getContent());
}
}
public
class
Client {
public
static
void
main(String[] args) {
// 创建一个报纸,作为目标
NewsPaper subject =
new
NewsPaper();
// 创建一个读者
Reader reader1 =
new
Reader();
Reader reader2 =
new
Reader();
// 注册订阅者
subject.addObserver(reader1);
subject.addObserver(reader2);
// 要出报纸啦
subject.setContent(
"New"
);
}
}
|
适配器模式的优缺点:
优点:实现了观察者和目标之间的抽象耦合,实现了动态联动,支持广播通信
缺点:可能会引起无谓的操作
4.思考
观察者模式的本质是:触发联动
当修改目标对象的状态时,就会触发相应的通知,然后会循环调用所有注册的观擦或者对象的相应方法,其实就相当于联动调用这些观察者的方法。
何时选用适配器模式
当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,可以选用观察者模式;
如果在更改一个对象的时候,需要同时连带改变其他的喜爱那个,而且不知道究竟应该有多少对象需要被连带改变,可以选用观察者模模式;
当一个对象必须通知其他对象,但是有希望这个对象和其他被通知的对象是松散耦合的,即这个对象其实不想知道具体被通知的对象,可以选用观察者模式。