设计模式-观察者模式
Posted studymorerich
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式-观察者模式相关的知识,希望对你有一定的参考价值。
2017-10-04 10:26:11
当我们在 github 上看到一个很好的项目时,也许会想要持续地跟进这个项目的最新进度。
当你按下 Watch 按钮时,每当项目更新,那么 github 就会不断地将这个项目的最新进展通过邮件发送给你,提醒你。
这就是我们今天谈论的 观察者模式。
请记住:任何模式的具体实现并不是一成不变的,根据具体的情况去改变
github 中有数据是我们所关心的,我们把 github 当作一个大主题,而我们则是观察者。
这个例子的 主题-观察者类图:
我们并不知道会有多少不同的主题会被别人关注,因此最好遵循 面向接口编程,不面向实现编程 这个原则
1 //主题 2 public interface Subject { 3 4 /** 5 * 注册成为观察者 6 * @param observer 观察者对象 7 */ 8 public void regitster(Observer observer); 9 10 /** 11 * 移除观察者 12 * @param observer 观察者对象 13 */ 14 public void remove(Observer observer); 15 16 /** 17 * 将最新消息通知观察者 18 */ 19 public void notifyObservers(); 20 }
上面这个接口是主题接口(也就是被观察者),很明显主题必须有添加、删除观察者以及将最新消息通知观察者等三个方法。
Observer 是观察者接口,主题要想添加或删除观察者,就要有观察者对象作为参数。
接下来的代码是观察者接口:
1 //观察者 2 public interface Observer { 3 /** 4 * 当主题通知时,观察者接收最新的数据以用来自己更新 5 * @param data 最新的数据 6 */ 7 public void update(Object data); 8 }
然后是具体的主题和观察者啦
现在在 javaWeb 中最有名的框架无疑就是 Spring 啦,我们就以 SpringSubject 作为具体主题的例子
1 //Spring 主题:SpringSubject 2 public class SpringSubject implements Subject { 3 4 /** 5 * 观察者列表 6 */ 7 private List<Observer> observers; 8 9 /** 10 * 主题持有的所有数据 11 */ 12 private String data; 13 14 public SpringSubject(){ 15 observers = new ArrayList<Observer>(); 16 } 17 18 /** 19 * 注册成为观察者 20 * 21 * @param observer 注册的观察者对象 22 */ 23 @Override 24 public void regitster(Observer observer) { 25 if(!observers.contains(observer)) { 26 observers.add(observer); 27 System.out.println(observer + "成为了 Spring 的观察者"); 28 } 29 } 30 31 /** 32 * 移除观察者 33 * 34 * @param observer 需要移除的观察者 35 */ 36 @Override 37 public void remove(Observer observer) { 38 if(observers.contains(observer)){ 39 observers.remove(observer); 40 System.out.println(observer + " 不再是 Spring 的观察者啦!"); 41 } 42 } 43 44 /** 45 * 将最新消息通知观察者 46 */ 47 @Override 48 public void notifyObservers() { 49 System.out.println("哇!有最新消息啦,赶快通知所有的观察者"); 50 for (Observer o : observers) { 51 o.update(data); 52 } 53 } 54 55 /** 56 * 当数据改变时 57 */ 58 public void setChanged(){ 59 notifyObservers(); 60 } 61 }
我们的猫咪观察者出场了!
1 //一个好奇的观察者 2 //这里有一只小猫咪对 Spring 很感兴趣,它于是成为了 Spring 的观察者! 3 public class CatObserver implements Observer { 4 5 /** 6 * 观察者所持有的数据 7 */ 8 private Object data; 9 10 /** 11 * 这里加上一个主题的域,如果有读者认为不需要的话,也可以自己实现一个 12 * 这个主题域,可以更方便的增加和删除观察者 13 */ 14 private Subject subject; 15 16 public CatObserver(Subject subject){ 17 this.subject = subject; 18 //猫咪如愿以偿的成为 spring 的粉丝啦 19 this.subject.regitster(this); 20 } 21 22 /** 23 * 小猫咪不想再当 spring 的观察者了!一点也不好玩 24 */ 25 public void remove(){ 26 this.subject.remove(this); 27 this.subject = null; 28 } 29 30 /** 31 * 当主题通知时,观察者接收最新的数据以用来自己更新 32 * 33 * @param data 最新的数据 34 */ 35 @Override 36 public void update(Object data) { 37 System.out.println("小猫咪接受到最新的数据啦"); 38 this.data = data; 39 } 40 }
到现在为止,我们就构建好了。最好就是测试代码啦
1 //这是测试代码 2 public class ObserverMain { 3 4 public static void main(String[] args) { 5 //Spring 主题诞生了! 6 Subject subject = new SpringSubject(); 7 //然后我们的猫咪,它一出来就迫不及待地成为 spring 主题的粉丝 8 Observer cat = new CatObserver(subject); 9 //数据改变 10 ((SpringSubject) subject).setChanged(); 11 //小猫咪退出了 12 ((CatObserver) cat).remove(); 13 } 14 }
这段代码显得很丑陋,强制转换用了两次,用 jdk5 出现的泛型修改下代码便可
测试结果如下:
控制通知的频率
听小猫咪说,它之所以退出,是因为 Spring 主题更新太频繁了
因此,我们现在修改下这个程序,Spring 主题每次更新一个大版本时在通知消息,小版本不通知
1 //下面是改进的版本:SpringSubject 2 【代码】 3 public class SpringSubject implements Subject { 4 5 /** 6 * 观察者列表 7 */ 8 private List<Observer> observers; 9 10 /** 11 * 主题所持有的所有数据 12 */ 13 private String data; 14 15 /** 16 * 是否通知 17 */ 18 private boolean isNotified = false; 19 20 public SpringSubject(){ 21 observers = new ArrayList<Observer>(); 22 } 23 24 /** 25 * 注册成为观察者 26 * 27 * @param observer 观察者对象 28 */ 29 @Override 30 public void regitster(Observer observer) { 31 if(!observers.contains(observer)) { 32 observers.add(observer); 33 System.out.println(observer + "成为了 Spring 的观察者"); 34 } 35 } 36 37 /** 38 * 移除观察者 39 * 40 * @param observer 41 */ 42 @Override 43 public void remove(Observer observer) { 44 if(observers.contains(observer)){ 45 observers.remove(observer); 46 System.out.println(observer + " 不再是 Spring 的观察者啦!"); 47 } 48 } 49 50 /** 51 * 将最新消息通知观察者 52 */ 53 @Override 54 public void notifyObservers() { 55 System.out.println("哇!有最新消息啦,赶快通知所有的观察者"); 56 for (Observer o : observers) { 57 o.update(data); 58 } 59 } 60 61 /** 62 * 在这里我们就可以控制更新的频率啦 63 */ 64 public void setNotified(){ 65 isNotified = true; 66 } 67 68 /** 69 * 当数据改变时 70 */ 71 public void setChanged(){ 72 setNotified(); 73 if(isNotified) { 74 notifyObservers(); 75 } 76 isNotified = false; 77 } 78 }
isNotified 这个域就是为了通知不那么频繁而设立的,实际情况根据需要使用
推拉
其实不一定要我们把数据 推送给 观察者,也可以观察者自己需要数据时,自己来拿,也很好的
这就是观察者模式的 推 和 拉 两种方式啦
推:主题会把自己所有的数据推送给任何一个观察者,不管这个观察者需不需要所有的数据
拉:每当观察者需要最新数据时,观察者都会自己去对应的主题拉走自己需要的数据
观察者模式的 拉 这个方式,由于观察者是主动的,所以 update 这个方法应该有主题的这个参数,下面重写 Observer 和 CatObserver:
1 //观察者 2 public interface Observer { 3 /** 4 * 当主题通知时,观察者接收最新的数据以用来自己更新 5 * 6 * @param data 最新的数据 7 */ 8 public void update(Object data); 9 10 /** 11 * 当观察者自己需要最新的数据时,观察者自己去主题内部取自己需要的数据 12 * @param subject 观察者依赖的数据的对应主题 13 */ 14 public void update(Subject subject); 15 }
1 public class CatObserver implements Observer { 2 3 /** 4 * 观察者所持有的数据 5 */ 6 private Object data; 7 8 /** 9 * 这里加上一个主题的域,如果有读者认为不需要的话,也可以自己实现一个 10 * 这里有主题的域,可以更方便的增加和删除观察者 11 */ 12 private Subject subject; 13 14 public CatObserver(Subject subject){ 15 this.subject = subject; 16 //猫咪如愿以偿的成为 spring 的粉丝啦 17 this.subject.regitster(this); 18 } 19 20 /** 21 * 小猫咪不想再当 spring 的观察者了!一点也不好玩 22 */ 23 public void remove(){ 24 this.subject.remove(this); 25 this.subject = null; 26 } 27 28 /** 29 * 当主题通知时,观察者接收最新的数据以用来自己更新 30 * 31 * @param data 最新的数据 32 */ 33 @Override 34 public void update(Object data) { 35 System.out.println("小猫咪接受到最新的数据啦"); 36 this.data = subject.data; 37 } 38 39 /** 40 * 当观察者自己需要最新的数据时,观察者自己去主题内部取自己需要的数据 41 */ 42 @Override 43 public void update(Subject subject) { 44 SpringSubject s = (SpringSubject)subject; 45 this.data = s.getData(); 46 } 47 /** 48 * 获取主题 49 * @return 50 * 观察者主题 51 */ 52 public Subject getSubject(){ 53 return this.subject; 54 } 55 }
最后的这个例子的类图如下:
这个模式我们就说完啦,来总结观察者啦
总结:
观察者模式:定义对象间一对多的依赖关系,当一个对象发生变化时,依赖其的多个对象都会收到通知并自动更新
观察者模式优点:
抽象主题只依赖于抽象观察者
观察者模式使信息产生层和响应层分离
以上是关于设计模式-观察者模式的主要内容,如果未能解决你的问题,请参考以下文章
Java设计模式补充:回调模式事件监听器模式观察者模式(转)