码迷,mamicode.com
首页 > 其他好文 > 详细

24种设计模式--观察者模式【Observer Pattern】

时间:2015-01-18 13:04:10      阅读:296      评论:0      收藏:0      [点我收藏+]

标签:

   《孙子兵法》有云: “知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好的一个办法,我们今天就来讲一个间谍的故事。

  韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在 2000 多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物,李斯,对,就是李斯,秦国的丞相,最终被残忍的车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了,韩非子早饭吃的什么,中午放了几个 P,晚上在做什么娱乐,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀! 好,我们先通过程序把这个过程展现一下,看看李斯是怎么监控韩非子,先看类图:

技术分享

  看惯了清一色的谈黄色类图,咱换个颜色,写程序是一个艺术创作过程,我的一个同事就曾经把一个类图画成一个小乌龟的形状,超级牛 X。这个类图应该是程序员最容易想到得,你要监控,我就给你监控,正确呀,我们来看程序的实现,先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色):

 1 package com.pattern.observer;
 2 
 3 /**
 4  * 类似韩非子这样的人,被监控起来了还不知道
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public interface IHanFeiZi {
 9     
10     // 韩非子也是人,也要吃早饭的
11     public void haveBreakfast();
12     
13     // 韩非子也是人,是人就要娱乐活动
14     public void haveFun();
15     
16 }
17 
18 // 然后看韩非子的实现类 HanFeiZi.java:
19 
20 package com.pattern.observer;
21 
22 /**
23  * 韩非子,李斯的师弟,韩国的重要人物
24  * @author http://www.cnblogs.com/initial-road/
25  *
26  */
27 public class HanFeiZi implements IHanFeiZi {
28     
29     // 韩非子是否在吃饭,作为监控的判断标准
30     private boolean isHaveBreakfast = false;
31     
32     // 韩非子是否在娱乐
33     private boolean isHaveFun = false;
34     
35     // 韩非子要吃饭了
36     public void haveBreakfast() {
37         System.out.println("韩非子:开始吃饭了...");
38         this.isHaveBreakfast = true;
39     }
40 
41     public void haveFun() {
42         System.out.println("韩非子:开始娱乐了...");
43         this.isHaveFun = true;
44     }
45 
46     public boolean isHaveBreakfast() {
47         return isHaveBreakfast;
48     }
49 
50     public void setHaveBreakfast(boolean isHaveBreakfast) {
51         this.isHaveBreakfast = isHaveBreakfast;
52     }
53 
54     public boolean isHaveFun() {
55         return isHaveFun;
56     }
57 
58     public void setHaveFun(boolean isHaveFun) {
59         this.isHaveFun = isHaveFun;
60     }
61     
62 }

   其中有两个 getter/setter 方法,这个就没有在类图中表示出来,比较简单,通过 isHaveBreakfast和 isHaveFun 这两个布尔型变量来判断韩非子是否在吃饭或者娱乐,韩非子是属于被观察者,那还有观察者李斯,我们来看李斯这类人接口:

 1 package com.pattern.observer;
 2 
 3 /**
 4  * 类似于李斯的这种人,现代嘛叫做偷窥狂
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public interface ILiSi {
 9     
10     // 一发现别人有动静,自己也要行动起来
11     public void update(String context);
12     
13 }

  李斯这类人比较简单,一发现自己观察的对象发生了变化,比如吃饭,娱乐了,自己立刻也要行动起来,那怎么行动呢?看实现类:

 1 package com.pattern.observer;
 2 
 3 /**
 4  * 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class LiSi implements ILiSi {
 9     
10     // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
11     public void update(String context) {
12         System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
13         this.reportToQinShiHuang(context);
14         System.out.println("李斯:汇报完毕,秦老板打赏他...\n");
15     }
16 
17     // 汇报秦始皇
18     private void reportToQinShiHuang(String reportContent){
19         System.out.println("李斯:报告,秦老板!韩非子有活动 ---> " + reportContent);
20     }
21 }

  韩非子是秦始皇非常崇拜的人物,甚至说过见韩非子一面死又何憾!不过,韩非子还真是被秦始皇干掉的,历史呀上演过太多这样的悲剧。这么重要的人物有活动,你李斯敢不向老大汇报?!

  两个重量级的人物都定义出来了,那我们就来看看要怎么监控,先写个监控程序:

 1 package com.pattern.observer;
 2 
 3 /**
 4  * 监控程序
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class Watch extends Thread {
 9     private HanFeiZi hanFeiZi;
10     private LiSi liSi;
11     private String type;
12     
13     // 通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么
14     public Watch(HanFeiZi _hanFeiZi, LiSi _liSi, String _type){
15         this.hanFeiZi = _hanFeiZi;
16         this.liSi = _liSi;
17         this.type = _type;
18     }
19     
20     public void run(){
21         while(true){
22             // 监控是否在吃早餐
23             if("breakfast".equals(this.type)){
24                 // 如果发现韩非子在吃饭,就通知李斯
25                 if(this.hanFeiZi.isHaveBreakfast()){
26                     this.liSi.update("韩非子在吃饭");
27                     // 重置状态,继续监控
28                     this.hanFeiZi.setHaveBreakfast(false);
29                 }
30             }else{    // 监控是否在娱乐
31                 if(this.hanFeiZi.isHaveFun()){
32                     this.liSi.update("韩非子在娱乐");
33                     this.hanFeiZi.setHaveFun(false);
34                 }
35             }
36         }
37     }
38     
39 }

  监控程序继承了 java.lang.Thread 类,可以同时启动多个线程进行监控,Java 的多线程机制还是比较简单的,继承 Thread 类,重写 run()方法,然后 new SubThread(),再然后 subThread.start()就可以启动一个线程了,我们继续往下看:

 1 package com.pattern.observer;
 2 
 3 /**
 4  * 这个Client就是我们,用我们的视角看待这段历史
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class Client {
 9     
10     public static void main(String[] args) throws InterruptedException {
11         // 定义出韩非子和李斯
12         LiSi liSi = new LiSi();
13         
14         HanFeiZi hanFeiZi = new HanFeiZi();
15         
16         // 观察早餐
17         Watch watchBreakfast = new Watch(hanFeiZi, liSi, "breakfast");
18         
19         // 开始启动线程,监控
20         watchBreakfast.start();
21         
22         // 观察娱乐情况
23         Watch watchFun = new Watch(hanFeiZi, liSi, "fun");
24         watchFun.start();
25         
26         // 然后这里我们看看韩非子在干什么
27         // 主线程等待1秒后再往下执行
28         Thread.sleep(1000);
29         hanFeiZi.haveBreakfast();
30         
31         // 韩非子娱乐了
32         Thread.sleep(1000);
33         hanFeiZi.haveFun();
34     }
35     
36 }

  结果出来,韩非子一吃早饭李斯就知道,韩非子一娱乐李斯也知道,非常正确!结果正确但并不表示你有成绩,我告诉你:你的成绩是 0,甚至是负的,你有没有看到你的 CPU 飙升,Eclipse 不响应状态?看到了?看到了你还不想为什么?!看看上面的程序,别的就不多说了,使用了一个 while(true)这样一个死循环来做监听,你要是用到项目中,你要多少硬件投入进来?你还让不让别人的程序也 run 起来?!一台服务器就跑你这一个程序就完事了,错,绝对的错!

  错误也看到了,我们必须要修改,这个没有办法应用到项目中去呀,而且这个程序根本就不是面向对象的程序,这完全是面向过程的(我写出这样的程序也不容易呀,安慰一下自己),不改不行,怎么修改呢?我们来想,既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子这里类上呢?说改就改,立马动手,我们来看修改后的类图:

技术分享

  类图非常简单,就是在 HanFeiZi 类中引用了 IliSi 这个接口,看我们程序代码怎么修改,IhanFeiZi接口完全没有修改,我们来看 HanFeiZi 这个实现类:

 1 package com.pattern.observer.advance;
 2 
 3 import com.pattern.observer.ILiSi;
 4 import com.pattern.observer.LiSi;
 5 
 6 /**
 7  * 韩非子,李斯的师弟,韩国的重要人物
 8  * @author http://www.cnblogs.com/initial-road/
 9  *
10  */
11 public class HanFeiZi {
12     
13     // 把李斯声明出来
14     private ILiSi liSi = new LiSi();
15     
16     // 韩非子要吃饭了
17     public void haveBreakfast(){
18         System.out.println("韩非子:开始吃饭了...");
19         // 通知李斯
20         this.liSi.update("韩非子在吃饭");
21     }
22     
23     // 韩非子开始娱乐,古代人没啥娱乐,你能想到的就那么多
24     public void haveFun(){
25         System.out.println("韩非子:开始娱乐了...");
26         this.liSi.update("韩非子在娱乐");
27     }
28 }

  韩非子 HanFeiZi 实现类就把接口的两个方法实现就可以了,在每个方法中调用 LiSi.update()方法,完成李斯观察韩非子任务,李斯的接口和实现类都没有任何改变,我们再来看看 Client 程序的变更:

 1 package com.pattern.observer.advance;
 2 
 3 /**
 4  * 这个Client就是我们,用我们的视角看待这段历史
 5  * @author http://www.cnblogs.com/initial-road/
 6  *
 7  */
 8 public class Client {
 9     
10     public static void main(String[] args) {
11         // 定义出韩非子
12         HanFeiZi hanFeiZi = new HanFeiZi();
13         
14         // 然后这里我们看看韩非子在干什么
15         hanFeiZi.haveBreakfast();
16         
17         // 韩非子娱乐了
18         hanFeiZi.haveFun();
19     }
20     
21 }

   李斯都不用在 Client 中定义了,非常简单。

  运行结果正确,效率也比较高,是不是应该乐呵乐呵了?大功告成了?稍等等,你想在战国争雄的时候,韩非子这么有名望(法家代表)、有实力(韩国的公子,他老爹参与过争夺韩国王位)的人,就只有秦国一个国家关心他吗?想想也不可能呀,肯定有一大帮的各国的类似李斯这样的人在看着他,监视着一举一动,但是看看我们的程序,你在 HanFeiZi 这个类中定义:

          技术分享

  一下子就敲死了,只有李斯才能观察到韩非子,这是不对的,也就是说韩非子的活动只通知了李斯一个人,这不可能;再者,李斯只观察韩非子的吃饭,娱乐吗?政治倾向不关心吗?思维倾向不关心吗?杀人放火不关心吗?也就说韩非子的一系列活动都要通知李斯,那可怎么办?要按照上面的例子,我们不是要修改疯掉了吗?这和开闭原则严重违背呀,我们的程序有问题,怎么修改,来看类图:

技术分享

   我们把接口名称修改了一下,这样显得更抽象化,Observable 是被观察者,就是类似韩非子这样的人,Observer 接口是观察者,类似李斯这样的,同时还有其他国家的比如王斯、刘斯等,在 Observable 接口中有三个比较重要的方法,分别是 addObserver 增加观察者,deleteObserver 删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象,看程序会更明白一点,先看 Observable 接口:

  1 package com.pattern.observer.advance2;
  2 
  3 /**
  4  * 所有被观察者,通用接口
  5  * @author http://www.cnblogs.com/initial-road/
  6  *
  7  */
  8 public interface Observable {
  9     
 10     // 增加一个观察者
 11     public void addObserver(Observer observer);
 12     
 13     // 删除一个观察者,我不想让你监控了
 14     public void deleteObserver(Observer observer);
 15 
 16     // 既然要观察,我发生改变了,他也应该用所动作--通知观察者
 17     public void notifyObservers(String context);
 18     
 19 }
 20 
 21 // 这是一个通用的被观察者接口,所有的被观察者都可以实现这个接口。再来看韩非子的实现类:
 22 
 23 package com.pattern.observer.advance2;
 24 
 25 import java.util.ArrayList;
 26 
 27 /**
 28  * 韩非子,李斯的师弟,韩国的重要人物
 29  * @author http://www.cnblogs.com/initial-road/
 30  *
 31  */
 32 public class HanFeiZi {
 33     
 34     // 定义一个变长数组,存放所有的观察者
 35     private ArrayList<Observer> observerList = new ArrayList<Observer>();
 36     
 37     // 增加观察者
 38     public void addObserver(Observer observer){
 39         this.observerList.add(observer);
 40     }
 41     
 42     // 删除观察者
 43     public void deleteObserver(Observer observer){
 44         this.observerList.remove(observer);
 45     }
 46     
 47     // 通知所有的观察者
 48     public void notifyObserver(String context){
 49         for(Observer observer : observerList){
 50             observer.update(context);
 51         }
 52     }
 53     
 54     // 韩非子要吃饭了
 55     public void haveBreakfast(){
 56         System.out.println("韩非子:开始吃饭了...");
 57         // 通知所有的观察者
 58         this.notifyObserver("韩非子在吃饭");
 59     }
 60     
 61     // 韩非子开始娱乐了,古代人没啥娱乐
 62     public void haveFun(){
 63         System.out.println("韩非子:开始娱乐了...");
 64         this.notifyObserver("韩非子在娱乐");
 65     }
 66     
 67 }
 68 
 69 // 再来看观察者接口 Observer.java:
 70 
 71 package com.pattern.observer.advance2;
 72 
 73 /**
 74  * 所有观察者,通用接口
 75  * @author http://www.cnblogs.com/initial-road/
 76  *
 77  */
 78 public interface Observer {
 79     
 80     // 一发现别人有动静,自己也要行动起来
 81     public void update(String context);
 82     
 83 }
 84 
 85 // 然后是三个很无耻的观察者,偷窥狂嘛:
 86 
 87 package com.pattern.observer.advance2;
 88 
 89 /**
 90  * 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 91  * @author http://www.cnblogs.com/initial-road/
 92  *
 93  */
 94 public class LiSi implements Observer {
 95     
 96     // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
 97     public void update(String context) {
 98         System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
 99         this.reportQinShiHuang(context);
100         System.out.println("李斯:汇报完毕了,秦老板大个赏...\n");
101     }
102 
103     private void reportQinShiHuang(String reportContext){
104         System.out.println("李斯:报告,秦老板!韩非子有活动了 ---> " + reportContext);
105     }
106 }
107 
108 // 李斯是真有其人,以下两个观察者是杜撰出来的:
109 
110 package com.pattern.observer.advance2;
111 
112 /**
113  * 王斯,也是观察者,杜撰的人名
114  * @author http://www.cnblogs.com/initial-road/
115  *
116  */
117 public class WangSi implements Observer {
118     
119     // 王斯,看到韩非子有活动,自己就受不了
120     public void update(String context) {
121         System.out.println("王斯:观察到韩非子有活动,自己也开始活动了...");
122         this.cry(context);
123         System.out.println("王斯:真的哭死了...\n");
124     }
125 
126     // 一看李斯有活动,就哭,痛苦
127     private void cry(String context){
128         System.out.println("王斯:因为" + context + ", --所以我悲伤呀!");
129     }
130 }
131 
132 package com.pattern.observer.advance2;
133 
134 /**
135  * 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
136  * 杜撰的人名
137  * @author http://www.cnblogs.com/initial-road/
138  *
139  */
140 public class LiuSi implements Observer {
141     
142     // 刘斯,观察到韩非子活动后,自己也做一定的事情
143     public void update(String context) {
144         System.out.println("刘斯:观察到韩非子活动,开始动作了...");
145         this.happy(context);
146         System.out.println("刘斯:真被乐死了\n");
147     }
148     
149     // 一看韩非子有变化,他就快乐
150     private void happy(String context){
151         System.out.println("刘斯:因为:" + context + ",---所以我快乐呀!");
152     }
153 }
154 
155 // 所有的历史人物都在场了,那我们来看看这场历史闹剧是如何演绎的:
156 
157 package com.pattern.observer.advance2;
158 
159 /**
160  * 这个Client就是我们,用我们的视角看待这段历史
161  * @author http://www.cnblogs.com/initial-road/
162  *
163  */
164 public class Client {
165     
166     public static void main(String[] args) {
167         // 三个观察者产生出来
168         Observer liSi = new LiSi();
169         Observer wangSi = new WangSi();
170         Observer liuSi = new LiuSi();
171         
172         // 定义出韩非子
173         HanFeiZi hanFeiZi = new HanFeiZi();
174         
175         // 我们后人根据历史,描述这个场景,有三个人在观察韩非子
176         hanFeiZi.addObserver(liSi);
177         hanFeiZi.addObserver(wangSi);
178         hanFeiZi.addObserver(liuSi);
179         
180         // 然后这里我们看看韩非子在干什么
181         hanFeiZi.haveBreakfast();
182         
183     }
184     
185 }

  好了,结果也正确了,也符合开闭原则了,也同时实现类间解耦,想再加观察者?好呀,继续实现Observer 接口就成了,这时候必须修改 Client 程序,因为你业务都发生了变化。

  细心的你可能已经发现,HanFeiZi 这个实现类中应该抽象出一个父类,父类完全实现接口,HanFeiZi这个类只实现两个方法 haveBreakfast 和 haveFun 就可以了,是的,是的,确实是应该这样,那先稍等等,我 们 打 开 JDK 的 帮 助 文 件 看 看 , 查 找 一 下 Observable 是 不 是 已 经 有 这 个 类 了? JDK 中 提 供 了 :java.util.Observable 实现类和 java.util.Observer 接口,也就是 说我们上面 写的那个例 子中 的Observable 接口可以改换成java.util.Observale 实现类了,看如下类图:

技术分享

  是不是又简单了很多?那就对了!然后我们看一下我们程序的变更,先看 HanFeiZi 的实现类:

 1 package com.pattern.observer.perfect;
 2 
 3 import java.util.Observable;
 4 
 5 /**
 6  * 韩非子,李斯的师弟,韩国的重要人物
 7  * @author http://www.cnblogs.com/initial-road/
 8  *
 9  */
10 public class HanFeiZi extends Observable {
11     
12     // 韩非子要吃饭了
13     public void haveBreakfast(){
14         System.out.println("韩非子:开始吃饭了...");
15         // 通知所有的观察者
16         super.setChanged();
17         super.notifyObservers("韩非子在吃饭");
18     }
19     
20     // 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
21     public void haveFun(){
22         System.out.println("韩非子:开始娱乐了...");
23         super.setChanged();
24         this.notifyObservers("韩非子在娱乐");
25     }
26     
27 }

  改变的不多,引入了一个 java.util.Observable 对象,删除了增加、删除观察者的方法,简单了很多,那我们再来看观察者的实现类:

 1 package com.pattern.observer.perfect;
 2 
 3 import java.util.Observable;
 4 import java.util.Observer;
 5 
 6 /**
 7  * 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 8  * @author http://www.cnblogs.com/initial-road/
 9  *
10  */
11 public class LiSi implements Observer {
12 
13     // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要想老板汇报
14     public void update(Observable observer, Object obj) {
15         System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
16         this.reportToQinShiHuang(obj.toString());
17         System.out.println("李斯:汇报完毕,秦老板打赏...\n");
18     }
19     
20     // 汇报给秦始皇
21     private void reportToQinShiHuang(String reportContext){
22         System.out.println("李斯:报告,秦老板!韩非子有活动了 --> " + reportContext);
23     }
24     
25 }

  就改变了部分代码, 应该 java.util.Observer 接口要求 update 传递过来两个变量,Observable 这个变量我们没用到,就不处理了。其他两个观察者实现类也是相同的改动,如下代码:

 1 package com.pattern.observer.perfect;
 2 
 3 import java.util.Observable;
 4 import java.util.Observer;
 5 
 6 /**
 7  * 王斯,也是观察者,杜撰的人名
 8  * @author http://www.cnblogs.com/initial-road/
 9  *
10  */
11 public class WangSi implements Observer {
12     
13     // 王斯,看到韩非子有活动,自己就受不了
14     public void update(Observable observable, Object obj) {
15         System.out.println("王斯:观察到韩非子有活动了,自己也开始活动了...");
16         this.cry(obj.toString());
17         System.out.println("王斯:真真的哭死了...\n");
18     }
19     
20     // 一看李斯有活动,就哭,痛苦
21     private void cry(String context){
22         System.out.println("王斯:因为" + context + ", --所以我悲伤呀!");
23     }
24     
25 }
26 
27 
28 package com.pattern.observer.perfect;
29 
30 import java.util.Observable;
31 import java.util.Observer;
32 
33 /**
34  * 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道
35  * 杜撰的人名
36  * @author http://www.cnblogs.com/initial-road/
37  *
38  */
39 public class LiuSi implements Observer {
40     
41     // 刘斯,观察到韩非子活动后,自己也做一定的事情
42     public void update(Observable observable, Object obj) {
43         System.out.println("刘斯:观察到韩非子有活动,开始动作了...");
44         this.happy(obj.toString());
45         System.out.println("刘斯:真被乐死了\n");
46     }
47     
48     // 一看韩非子有变化,他就快乐
49     private void happy(String context){
50         System.out.println("刘斯:因为" + context + ", ---所以我快乐呀!");
51     }
52 }
53 
54 // 然后再来看 Client 程序:
55 
56 package com.pattern.observer.perfect;
57 
58 import java.util.Observer;
59 
60 /**
61  * 这个Client就是我们,用我们的视角看待这段历史
62  * @author http://www.cnblogs.com/initial-road/
63  *
64  */
65 public class Client {
66     
67     public static void main(String[] args) {
68         // 三个观察者产生出来
69         Observer liSi = new LiSi();
70         Observer wangSi = new WangSi();
71         Observer liuSi = new LiuSi();
72         
73         // 定义出韩非子
74         HanFeiZi hanFeiZi = new HanFeiZi();
75         
76         // 我们后人根据历史,描述这个场景,有三个人在观察韩非子
77         hanFeiZi.addObserver(liSi);
78         hanFeiZi.addObserver(wangSi);
79         hanFeiZi.addObserver(liuSi);
80         
81         // 然后这里我们看看韩非子在干什么
82         hanFeiZi.haveBreakfast();
83         
84     }
85     
86 }

  程序体内没有任何变更,只是引入了一个接口而已。

  运行结果一样,只是通知的先后顺序不同而已,程序已经简约到极致了。以上讲解的就是观察者模式,这个模式的通用类图如下:

 技术分享

 

  观察者模式在实际项目的应用中非常常见,比如你到 ATM 机器上取钱,多次输错密码,卡就会被 ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化 ATM 机屏幕,返回最初状态,你不能因为就吞了一张卡,整个 ATM 都不能用了吧,一般前两个动作都是通过观察者模式来完成的。

  观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe) ,如果你做过 EJB(EnterpriseJavaBean)的开发,这个你绝对不会陌生。EJB2是个折腾死人不偿命的玩意儿,写个 Bean 要实现,还要继承,再加上那一堆的配置文件,小项目还凑活,你要知道用 EJB 开发的基本上都不是小项目,到最后是每个项目成员都在骂 EJB 这个忽悠人的东西;但是 EJB3 是个非常优秀的框架,还是算比较轻量级,写个 Bean只要加个 Annotaion 就成了,配置文件减少了,而且也引入了依赖注入的概念,虽然只是 EJB2 的翻版,但是毕竟还是前进了一步,不知道以后 EJB 的路会怎么样。 在 EJB 中有三个类型的 Bean:Session Bean、EntityBean 和 MessageDriven Bean,我们这里来说一下 MessageDriven Bean(一般简称为 MDB),消息驱动 Bean,消息的发布者(Provider)发布一个消息,也就是一个消息驱动 Bean,通过 EJB 容器(一般是 Message Queue消息队列)通知订阅者做出回应,从原理上看很简单,就是观察者模式的升级版。

  那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是观察者模式非常重要的功能。使用观察者模式也有两个重点问题要解决:广播链的问题。如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一(传递两次),这还是比较好控制的;

  异步处理问题。这个 EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看 Message Queue,就会有更深的了解。

  我们在来回顾一下我们写的程序,观察者增加了,我就必须修改业务逻辑 Client 程序,这个是必须得吗?回顾一下我们以前讲到工厂方法模式的时候用到了 ClassUtils 这个类,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!我可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了!

24种设计模式--观察者模式【Observer Pattern】

标签:

原文地址:http://www.cnblogs.com/initial-road/p/observer_pattern.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!