结合案例分析java中的观察者模式

首先看一下java中已经定义好了的观察者类(Observer)、被观察者类(Observable)的结构:

1 // 观察者类
2 public interface Observer {
3 // 此方法用于定义观察者观察到变化后发生的行为
4 // 第一个参数是被观察者;第二个参数是一个可变对象,方便动态传递某些信息
5 void update(Observable o, Object arg);
6 }
复制代码
1 public class Observable {
2 // 变动标识,用于判断被观察者是否有变化
3 private boolean changed = false;
4 // 存放观察者
5 private Vector obs;
6
7 public Observable() {
8 obs = new Vector<>();
9 }
10   
11 public synchronized void addObserver(Observer o) {
12 if (o == null)
13 throw new NullPointerException();
14 if (!obs.contains(o)) {
15 obs.addElement(o);
16 }
17 }
18
19 public synchronized void deleteObserver(Observer o) {
20 obs.removeElement(o);
21 }
22
23 public void notifyObservers() {
24 notifyObservers(null);
25 }
26 // 通知被观察者发生了变化,循环调用update方法 ☆
27 public void notifyObservers(Object arg) {
28 Object[] arrLocal;
29
30 synchronized (this) {
31 if (!changed)
32 return;
33 arrLocal = obs.toArray();
34 clearChanged();
35 }
36
37 for (int i = arrLocal.length-1; i>=0; i–)
38 ((Observer)arrLocal[i]).update(this, arg);
39 }
40
41 public synchronized void deleteObservers() {
42 obs.removeAllElements();
43 }
44 // 将改变状态设置成已改变
45 protected synchronized void setChanged() {
46 changed = true;
47 }
48
49 protected synchronized void clearChanged() {
50 changed = false;
51 }
52
53 public synchronized boolean hasChanged() {
54 return changed;
55 }
56
57 public synchronized int countObservers() {
58 return obs.size();
59 }
60 }
复制代码
可见被观察者类中维护了一个观察者的列表,在发生变化通知观察者时是循环列表,调用每个观察者的update方法,具体每个观察者是如何做的,取决于其update方法。

    知道了java中的观察者和被观察者类,我们要如何使用呢?且看下面的例子。

    十一回来的路上看了好几篇半佛仙人的文章,略有感悟,于是此次就用仙人的案例作为主体进行设计。仙人是一个风控出身的"社会工程学专家",热衷于写文章揭露社会上的沙雕事件、违法事件。所以此处观察者就是仙人,而被观察者则是违法事件,下面是定义出来的两个类:

复制代码
1 /**
2 * 仙人是观察者,实现Observer观察者接口
3 /
4 public class CelestialBeing implements Observer {
5
6 @Override
7 public void update(Observable o, Object arg) {
8 writeToPen();
9 }
10 // 写文章揭露(也就是喷)
11 private void writeToPen(){
12 System.out.println(“写文章批斗、吊打”);
13 }
14 }
复制代码
复制代码
1 /
*
2 * 违法事件是被观察者,一旦出现变化,会被半佛仙人观察到
3 */
4 public class IllegalThing extends Observable {
5
6 public void change() {
7 System.out.println(“出现了违法事件”);
8 super.setChanged();
9 }
10 }
复制代码
下面是测试类:

复制代码
1 public class TestClient {
2
3 public static void main(String[] args) {
4 IllegalThing illegalThing = new IllegalThing();
5 /**
6 * 观察者模式的关键点:被观察者持有观察者
7 */
8 illegalThing.addObserver(new CelestialBeing());
9 // 有改变
10 illegalThing.change();
11 // 通知所有观察者
12 illegalThing.notifyObservers();
13
14 }
15 }
复制代码
运行结果:

1 出现了违法事件
2 写文章批斗、吊打
可以看到,由于被观察者类Observable自身已经维护好了观察者列表,所以我们的被观察者类不需要做太多的事情,只需要将setChanged方法暴露出去即可。观察者模式的关键,就是上面注释中提到的:被观察者持有观察者列表。只要注意了这一点,相信大家在实际场景中使用时便不会用错。

二、看看观察者模式在Spring中的应用

1、在Spring容器的某个阶段触发事件

    如果我想在Spring容器refresh之后触发某些特定逻辑,那么定义一个这样的类就可以:

复制代码
1 @Component
2 public class MySpringListener implements ApplicationListener {
3
4 @Override
5 public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
6 System.out.println(“MySpringObserver执行了”);
7 // 执行自定义逻辑
8 }
9 }
复制代码
能如此方便的做到,主要是因为Spring帮我们创建好了refresh的事件,在Spring容器过程中类似的事件有以下几个:

2、自定义的事件触发

    如果我们想在某些特定的时机触发一个自定义的事件,比如在发邮件时触发一个事件监听,那么在Spring中要怎么做呢?

首先定义一个邮件事件:

1 public class MySpringEmailEvent extends ApplicationEvent {
2
3 public MySpringEmailEvent(Object source) {
4 super(source);
5 }
6 }
复制代码
1 @Component
2 public class MySpringEmailListener implements ApplicationListener {
3
4 @Override
5 public void onApplicationEvent(MySpringEmailEvent mySpringEmailEvent) {
6 System.out.println(“MySpringEmailListener执行了”);
7 // 执行自定义逻辑
8 }
9 }
复制代码
复制代码
1 @Component
2 public class MyEventTrigger {
3 @Autowired
4 private ApplicationContext applicationContext;
5 // 触发事件
6 public void sendEmail () {
7 applicationContext.publishEvent(new MySpringEmailEvent(applicationContext));
8 }
9 }
复制代码
测试类:

复制代码
1 public class SpringClient {
2 public static void main(String[] args) {
3 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
4 MyEventTrigger bean = applicationContext.getBean(MyEventTrigger.class);
5 bean.sendEmail();
6 }
7 }
复制代码
执行结果:

1 MySpringEmailListener执行了
东莞网站建设www.zg886.cn

发布了0 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ting2909/article/details/102372348
今日推荐