先来看下下报纸和杂志的订阅:
(1)报社:出版报纸和杂志
(2)订阅者:向某家报社订阅报纸和杂志,只要报社出版了新的报纸,订阅者就会收到最新的报纸和杂志。
(3)报社具有添加和删除订阅者的功能(其实应该是订阅者具有订阅和退订的功能,这个主动权应该是订阅者而不是报社,报社也应该对外开放这样的方法)
下面就让我们来简单实现上述的描述:
报社:PublishingHouse 如下:
PublishingHouse 有添加和删除订阅者Subscriber的方法,同时在生产报纸时来通知所有的订阅者。
订阅者:Subscriber如下:
订阅者是一个接口,订阅到报纸后如何处理不同的订阅者有不同的实现。
如某类订阅者 ASubscriber:
再如某类订阅者 BSubscriber:
测试方法如下:
输出的结果为:
观察者模式其实很简单,就是报社维护了一个订阅者集合,一旦有新的报纸,就会去遍历那个集合,调用集合里元素的通用方法receiveNewspaper(接口方法)。报社的produceNewspaper方法就是将上述过程进行了封装而已。
上述的例子很简单,同时有很多问题。下面来说说JDK自身已实现的观察者模式:
被观察者Observable(如报社),观察者Observer(如订阅者)
观察者Observer如下:
被观察者Observable如下:
它使用一个Vector集合来存放所有的Observer,具有添加删除Observer的功能。同时还有一个changed属性,用来标示状态是否发生变化,所以在执行notifyObservers通知前要把该状态改为true,为了保证线程安全,setChanged、hasChanged、clearChanged都加上了synchronized 进行同步。再来详细看下notifyObservers过程:
arrLocal作为当时Observers的一个快照,在该方法中对状态的判断和改变进行了同步,然后按照倒序进行了事件的通知,即调用集合中每个Observer。
先说下我对jdk实现的观察者模式的看法:
(1)加入changed属性,则需要在每次想要执行通知前,提前设置setChanged即changed=true,然后执行notifyObservers才有效,这使得我们在使用时必须使setChanged方法和notifyObservers方法进行synchronized操作,使之成为“原子操作”,不然在多线程环境中,会通知失败。如线程1执行setChanged,线程2执行setChanged,线程1执行notifyObservers,它就会把changed=false,此时线程2再执行notifyObservers,就会因为changed=false而直接退出,不会发出通知,changed属性的确给我们带来了同步的麻烦,也体现在了notifyObservers方法中,如下:
有了changed属性,必须要在该方法中对changed的判断进行同步,否则会造成如下现象:线程1设置了setChanged方法,在执行notifyObservers时还未执行到clearChanged方法,此时线程2不用调用setChanged方法,直接调用notifyObservers,也会实现通知。
(2)Observer接口的方法,void update(Observable o, Object arg);此时传递的是Observable对象,如MyObservable继承了Observable 对象,此时我们要获取MyObservable的数据就必须要对void update(Observable o, Object arg)传进来的Observable o进行强制类型转换,而我们应该可以利用泛型来更好的设计这一个接口,避免强制类型转换。如下:
(3)对于Observable的notifyObservers通知顺序,默认写死为倒序通知,不应该是这样的,而是,应该对开开放成接口,同时提供多种实现,如正序通知的实现和倒序通知的实现。如果还满足不了需求,我们就可以自定义顺序实现该接口,设置进Observable中。
(4)对于Observable内部使用Vector,也是不可扩展的一个地方,如果我们想换种集合就没法实现,并不是什么需求都会去使用Vector。
(5)观察者自己应该具有取消关注的权利,即微信中的订阅一样,自己有权利去关注和取消关注,所以有时观察者接口由希望有一个取消关注的方法,然而该方法的实现又很明白,不需要观察者自己来实现。这就使用到了java8中接口中默认方法的实现。如下所示:
不知道这样写对不对,
所以对于Jdk自身实现的观察者模式扩展性并不好,同时大量使用synchronized来解决多线程问题,没有更好的使用多线程包中的同步技术。观察者模式本身很简单,所以有时候还是需要我们自己来写一个观察者模式。
以上纯属个人拙见,欢迎激烈讨论,共同进步。
(1)报社:出版报纸和杂志
(2)订阅者:向某家报社订阅报纸和杂志,只要报社出版了新的报纸,订阅者就会收到最新的报纸和杂志。
(3)报社具有添加和删除订阅者的功能(其实应该是订阅者具有订阅和退订的功能,这个主动权应该是订阅者而不是报社,报社也应该对外开放这样的方法)
下面就让我们来简单实现上述的描述:
报社:PublishingHouse 如下:
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
|
package
com.lg.design.obser;
import
java.util.ArrayList;
import
java.util.List;
public
class
PublishingHouse {
private
List<Subscriber> subscribers=
new
ArrayList<Subscriber>();
public
void
add(Subscriber subscriber){
if
(subscriber!=
null
&& !subscribers.contains(subscriber)){
subscribers.add(subscriber);
}
}
public
void
delete(Subscriber subscriber){
if
(subscriber!=
null
){
subscribers.remove(subscriber);
}
}
public
void
produceNewspaper(String newspaper) {
notifySubscribers(newspaper);
}
private
void
notifySubscribers(String newspaper) {
for
(Subscriber subscriber:subscribers){
subscriber.receiveNewspaper(newspaper);
}
}
}
|
PublishingHouse 有添加和删除订阅者Subscriber的方法,同时在生产报纸时来通知所有的订阅者。
订阅者:Subscriber如下:
1
2
3
4
5
6
7
|
package
com.lg.design.obser;
public
interface
Subscriber {
public
void
receiveNewspaper(String newspaper);
}
|
订阅者是一个接口,订阅到报纸后如何处理不同的订阅者有不同的实现。
如某类订阅者 ASubscriber:
1
2
3
4
5
6
7
8
9
10
|
package
com.lg.design.obser;
public
class
ASubscriber
implements
Subscriber{
@Override
public
void
receiveNewspaper(String newspaper) {
System.out.println(
"A receive newspaper:"
+newspaper);
}
}
|
再如某类订阅者 BSubscriber:
1
2
3
4
5
6
7
8
9
10
|
package
com.lg.design.obser;
public
class
BSubscriber
implements
Subscriber{
@Override
public
void
receiveNewspaper(String newspaper) {
System.out.println(
"B receive newspaper:"
+newspaper);
}
}
|
测试方法如下:
1
2
3
4
5
6
7
8
9
10
|
public
static
void
main(String[] args){
PublishingHouse publishingHouse=
new
PublishingHouse();
Subscriber a=
new
ASubscriber();
Subscriber b=
new
BSubscriber();
publishingHouse.add(a);
publishingHouse.add(b);
publishingHouse.produceNewspaper(
"第一天的报纸"
);
publishingHouse.produceNewspaper(
"第二天的报纸"
);
}
|
输出的结果为:
1
2
3
4
|
A receive newspaper:第一天的报纸
B receive newspaper:第一天的报纸
A receive newspaper:第二天的报纸
B receive newspaper:第二天的报纸
|
观察者模式其实很简单,就是报社维护了一个订阅者集合,一旦有新的报纸,就会去遍历那个集合,调用集合里元素的通用方法receiveNewspaper(接口方法)。报社的produceNewspaper方法就是将上述过程进行了封装而已。
上述的例子很简单,同时有很多问题。下面来说说JDK自身已实现的观察者模式:
被观察者Observable(如报社),观察者Observer(如订阅者)
观察者Observer如下:
1
2
3
4
|
public
interface
Observer {
void
update(Observable o, Object arg);
}
|
被观察者Observable如下:
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
72
73
74
|
package
java.util;
public
class
Observable {
private
boolean
changed =
false
;
private
Vector obs;
public
Observable() {
obs =
new
Vector();
}
public
synchronized
void
addObserver(Observer o) {
if
(o ==
null
)
throw
new
NullPointerException();
if
(!obs.contains(o)) {
obs.addElement(o);
}
}
public
synchronized
void
deleteObserver(Observer o) {
obs.removeElement(o);
}
public
void
notifyObservers() {
notifyObservers(
null
);
}
public
void
notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized
(
this
) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if
(!changed)
return
;
arrLocal = obs.toArray();
clearChanged();
}
for
(
int
i = arrLocal.length-
1
; i>=
0
; i--)
((Observer)arrLocal[i]).update(
this
, arg);
}
public
synchronized
void
deleteObservers() {
obs.removeAllElements();
}
protected
synchronized
void
setChanged() {
changed =
true
;
}
protected
synchronized
void
clearChanged() {
changed =
false
;
}
public
synchronized
boolean
hasChanged() {
return
changed;
}
public
synchronized
int
countObservers() {
return
obs.size();
}
}
|
它使用一个Vector集合来存放所有的Observer,具有添加删除Observer的功能。同时还有一个changed属性,用来标示状态是否发生变化,所以在执行notifyObservers通知前要把该状态改为true,为了保证线程安全,setChanged、hasChanged、clearChanged都加上了synchronized 进行同步。再来详细看下notifyObservers过程:
arrLocal作为当时Observers的一个快照,在该方法中对状态的判断和改变进行了同步,然后按照倒序进行了事件的通知,即调用集合中每个Observer。
先说下我对jdk实现的观察者模式的看法:
(1)加入changed属性,则需要在每次想要执行通知前,提前设置setChanged即changed=true,然后执行notifyObservers才有效,这使得我们在使用时必须使setChanged方法和notifyObservers方法进行synchronized操作,使之成为“原子操作”,不然在多线程环境中,会通知失败。如线程1执行setChanged,线程2执行setChanged,线程1执行notifyObservers,它就会把changed=false,此时线程2再执行notifyObservers,就会因为changed=false而直接退出,不会发出通知,changed属性的确给我们带来了同步的麻烦,也体现在了notifyObservers方法中,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
void
notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;
synchronized
(
this
) {
if
(!changed)
return
;
arrLocal = obs.toArray();
clearChanged();
}
for
(
int
i = arrLocal.length-
1
; i>=
0
; i--)
((Observer)arrLocal[i]).update(
this
, arg);
}
|
有了changed属性,必须要在该方法中对changed的判断进行同步,否则会造成如下现象:线程1设置了setChanged方法,在执行notifyObservers时还未执行到clearChanged方法,此时线程2不用调用setChanged方法,直接调用notifyObservers,也会实现通知。
(2)Observer接口的方法,void update(Observable o, Object arg);此时传递的是Observable对象,如MyObservable继承了Observable 对象,此时我们要获取MyObservable的数据就必须要对void update(Observable o, Object arg)传进来的Observable o进行强制类型转换,而我们应该可以利用泛型来更好的设计这一个接口,避免强制类型转换。如下:
1
2
3
4
|
public
interface
MyObserver<T
extends
Observable>{
public
void
update(T t,Object args);
}
|
(3)对于Observable的notifyObservers通知顺序,默认写死为倒序通知,不应该是这样的,而是,应该对开开放成接口,同时提供多种实现,如正序通知的实现和倒序通知的实现。如果还满足不了需求,我们就可以自定义顺序实现该接口,设置进Observable中。
(4)对于Observable内部使用Vector,也是不可扩展的一个地方,如果我们想换种集合就没法实现,并不是什么需求都会去使用Vector。
(5)观察者自己应该具有取消关注的权利,即微信中的订阅一样,自己有权利去关注和取消关注,所以有时观察者接口由希望有一个取消关注的方法,然而该方法的实现又很明白,不需要观察者自己来实现。这就使用到了java8中接口中默认方法的实现。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
interface
Observer {
void
update(Observable o, Object arg);
default
void
unregister(Observable o){
o.deleteObserver(
this
);
}
default
void
register(Observable o){
o.addObserver(
this
);
}
}
|
不知道这样写对不对,
所以对于Jdk自身实现的观察者模式扩展性并不好,同时大量使用synchronized来解决多线程问题,没有更好的使用多线程包中的同步技术。观察者模式本身很简单,所以有时候还是需要我们自己来写一个观察者模式。
以上纯属个人拙见,欢迎激烈讨论,共同进步。