Head First设计模式之观察者(Observer)模式(二)
小白将从以下几点对观察者模式进行解析
PS:有趣的事情发生时,可千万不要错误^_^
-
观察者模式引入的设计技巧
-
观察者模式的定义及类图
-
观察者模式的优缺点
-
观察者模式的应用场景
-
观察者模式实战
-
总结
零、观察者模式引入的技巧
先过一遍,放入脑海中,等看完后面所有的内容,可以结合上下文进行消化
我们要做的不是看过、读过、理解过、消化过,而是需要将其平常化,使用模式就像使用变量一样简单。
- 为了交互对象之间的松耦合设计而努力(松耦合的设计能够让我们建立有弹性的OO系统,能够应对变化)
一、观察者模式的定义及类图
定义:在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
类图:类图中一共有四个角色,主题、观察者、具体主题、具体观察者
- 主题(Subject):主题是一个接口,该接口规定了具体主题需要实现的方法,比如,添加、删除观察者以及通知观察者更新数据的方法。
- 观察者(Observer):观察者是一个接口,该接口规定了具体观察者用来更新数据的方法。
- 具体主题(ConcreteSubject):具体主题是实现主题接口类的一个实例,该实例包含有可以经常发生变化的数据。具体主题需使用一个集合,比如ArrayList,存放观察者的引用,以便数据变化时通知具体观察者。
- 具体观察者(ConcreteObserver):具体观察者是实现观察者接口类的一个实例。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,使自己成为它的观察者,或让这个具体主题将自己从具体主题的集合中删除,使自己不再是它的观察者。
以下是UML类图
二、策略模式的优缺点
优点:
- 解除耦合,让耦合的双方都依赖于抽象,从而使各自的变化不会影响到另一边(除设计上的变化外)
缺点:
1. 如果观察者的对象非常多时,通知到所有的观察者所花费的时间会比较多,有可能还没通知完,被观察者的状态又发生变化了,其实看到这,你会联想到MQ 消息队列,观察者的push 模式可以参考MQ (好好思考下)
2. 如果观察者和被观察者之间存在循环依赖关系的话,将会导致系统奔溃
3.观察者可以随时知道被观察者对象发生了变化,但是观察者模式不知道被观察者对象是如何发生变化的,当然不关系就没有什么问题
三、策略模式的应用场景
- 配置中心, 相信这个都不陌生, 当配置发现变更时,所有监听的客户端都能拿到最新的配置。配置中心 可以做成pull 模式 或者 push 模式
- 报纸订阅服务,现实生活中,只要你订阅了报纸,那么报社每次出版新报纸,就会送到你家门口
四、策略模式实战
以下代码不会太复杂,只是作为示例,我这边以配置中心来进行实战
主题(Subject)角色设计如下:
/** * @Author: wander * @Descripttion: * @Date: 2018年07月17日09时49分 */ public interface Subject { void addObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); }
观察者(Observer)角色设计如下:
/** * @Author: wander * @Descripttion: * @Date: 2018年07月17日09时46分 */ public interface Observer { void update(String config); }
具体观察者 activity 设计 如下:
/** * @Author: wander * @Descripttion: 活动项目观察者 * @Date: 2018年07月17日09时52分 */ public class ActivityObserver implements Observer { @Override public void update(String config) { System.out.println("activity 项目接收到了新配置:" + config); } }
具体观察者 cms 设计 如下:
/** * @Author: wander * @Descripttion: * @Date: 2018年07月17日09时53分 */ public class CmsObserver implements Observer { @Override public void update(String config) { System.out.println("cms 项目接收到了新配置:" + config); } }
配置主题设计如下:
import java.util.ArrayList; import java.util.List; /** * @Author: wander * @Descripttion: 配置 主题 * @Date: 2018年07月17日09时54分 */ public class ConfigSubject implements Subject{ private List<Observer> observers = new ArrayList<>(); private String config = "哈哈哈"; @Override public void addObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { if (observers.size() <= 0) return; observers.remove(observer); } @Override public void notifyObservers() { observers.forEach(observer -> observer.update(config)); } public void setConfig(String config) { this.config = config; } }
运行环境设计如下:
/** * @Author: wander * @Descripttion: * @Date: 2018年07月17日09时59分 */ public class Main { public static void main(String[] args) { //新建配置主题 ConfigSubject subject = new ConfigSubject(); //创建观察者 并 监听配置主题 Observer activity = new ActivityObserver(); Observer cms = new CmsObserver(); subject.addObserver(activity); subject.addObserver(cms); //配置主题 更新 消息了 通知所有的 观察者 subject.setConfig("windows default"); subject.notifyObservers(); } }
运行结果如下:
activity 项目接收到了新配置:windows default cms 项目接收到了新配置:windows default
五、总结
观察者模式,又可以称之为发布-订阅模式,观察者,顾名思义,就是一个监听者,类似监听器的存在,一旦被观察/监听的目标发生的情况,就会被监听者所知道。
目前一共积累了4种设计技巧
-
封装变化(把会变化的部分取出并“封装”起来,好让其他部分不会受到影响,这样一来,代码变化引起的不经意后果变少,系统变得更有弹性)
-
针对接口编程,而不是针对实现编程(可以把具体的实现延迟到”运行时“,执行时会根据实际状况执行到真正的行为,不会在编译时直接绑死,充分利用多态使系统更有弹性)
-
多用组合,少用继承("有一个”可能比“是一个”更好,可以在“运行时”动态的改变具体的实现,这样使系统有很大的弹性)
- 为了交互对象之间的松耦合设计而努力(松耦合的设计能够让我们建立有弹性的OO系统,能够应对变化)