设计模式系列<Java实现>-观察者模式

观察者模式

观察者模式和监听者模式非常相似,相比之下,观察者模式的结构更加简单一些。观察者模式包含四个角色:

  • 抽象被观察者角色 (一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,相当于监听者模式中的事件源和事件对象的结合)
  • 抽象观察者角色 (为所有的具体观察者定义一个接口,在得到主题通知时更新自己,相当于监听者模式中的监听器)
  • 具体被观察者角色
  • 具体观察者角色

JDK为观察者模式提供了一个Observable类作为被观察者,一个Observer接口作为观察者。其中Observable类不像监听者模式中的事件源那样需要自己去实现,JDK已经帮我们预先定义了其中的一些通用功能,例如存储观察者的集合,往集合中添加观察者、删除观察者、通知观察者等等,并且由synchronized保证线程安全。

这里也举一个生活中常见的例子,就是微信公众号,比如我事先注册一个微信公众号,然后有不同的用户来订阅这个公众号,那么我们要实现微信公众号发布新文章的时候,所有已订阅此公众号的用户都能接收到文章推送。这儿的微信公众号作为被观察者,那么用户就是订阅者,即观察者。

首先继承JDK提供的Observable类实现被观察者,也就是微信公众号的超类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class WeChatSubscription extends Observable {
private String name;

public WeChatSubscription(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void publishArticles(String content) {
setChanged();
notifyObservers(content);
}
}

这里需要自己派生一个publishArticles函数用于新文章的发布,注意一下在notifyObservers()调用前还需要调用一下setChanged()方法,至于原因我们看一下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
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);
}

如果不先setChanged()的话notifyObservers()就会直接return,不会通知观察者。

接下来再定义用户,即观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Subscriber implements Observer {
private String name;

public Subscriber(String name) {
this.name = name;
}

public String getName() {
return name;
}
@Override
public void update(Observable o, Object arg) {
WeChatSubscription wx = (WeChatSubscription) o;
System.out.println(name + "收到了<" + wx.getName() + ">发布的一篇新文章:" + arg);
}
}

最后主函数测试一下

1
2
3
4
5
6
7
8
9
public class Main {

public static void main(String[] args) {
WeChatSubscription wx = new WeChatSubscription("经典美文");
Subscriber user1 = new Subscriber("user1");
wx.addObserver(user1);
wx.publishArticles("我有一只小毛驴");
}
}

上面我先注册一个名字叫做经典美文的公众号,然后让user1订阅这个公众号,最后用公众号发布了一篇旷世美文《我有一只小毛驴》,来看看输出

user1收到了<经典美文>发布的一篇新文章:我有一只小毛驴

完美,再增加user2用户来订阅一下看看

1
2
3
4
5
6
7
8
9
10
11
public class Main {

public static void main(String[] args) {
WeChatSubscription wx = new WeChatSubscription("经典美文");
Subscriber user1 = new Subscriber("user1");
Subscriber user2 = new Subscriber("user2");
wx.addObserver(user1);
wx.addObserver(user2);
wx.publishArticles("我有一只小毛驴");
}
}

看看输出

user2收到了<经典美文>发布的一篇新文章:我有一只小毛驴
user1收到了<经典美文>发布的一篇新文章:我有一只小毛驴

完美,至于收到推送的顺序为什么user2在前是由于Observable类中的观察者列表是从后往前读取的,所以后订阅的会先收到消息推送。

最后我们把之前的user1用户取消订阅再来看看效果

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {

public static void main(String[] args) {
WeChatSubscription wx = new WeChatSubscription("经典美文");
Subscriber user1 = new Subscriber("user1");
Subscriber user2 = new Subscriber("user2");
wx.addObserver(user1);
wx.addObserver(user2);
wx.deleteObserver(user1);
wx.publishArticles("我有一只小毛驴");
}
}

user2收到了<经典美文>发布的一篇新文章:我有一只小毛驴

这下就只有user2能够收到通知了,OK,收工。

文章作者: Shawn Qin
文章链接: https://qinshuang1998.github.io/2019/02/04/design-pattern-02/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Shawn's Blog