博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
观察者模式
阅读量:7069 次
发布时间:2019-06-28

本文共 5823 字,大约阅读时间需要 19 分钟。

订购杂志案例

场景

小明和小刘十分喜欢一款名为《电脑爱好者》的杂志,就像公众号的推送一样,他们想得到这款杂志的出版消息,然后去书店购买。

clipboard.png

笨拙的实现

当前杂志

/** * 当前杂志/最新杂志 */public class CurrentMagazine {    private String name;              // 当前杂志名称    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;        this.inform();                // 杂志更新时调用inform方法通知小明和小刘    }    private void inform() {        XiaoMing.update(this);        XiaoLiu.update(this);    }}

小明与小刘实现相关update方法,当杂志更新时通知小明和小刘。

/** * 小明 */public class XiaoMing {    public static void update(CurrentMagazine magazine) {        System.out.println("小明:" + magazine.getName() + "出版了,我要去书店购买。");    }}/** * 小刘 */public class XiaoLiu {    public static void update(CurrentMagazine magazine) {        System.out.println("小刘:" + magazine.getName() + "出版了,我要去书店购买。");    }}

main方法:

public class Main {    public static void main(String[] args) {        CurrentMagazine currentMagazine = new CurrentMagazine();        currentMagazine.setName("电脑爱好者2018年第10期");        currentMagazine.setName("电脑爱好者2018年第11期");    }}

运行结果:

clipboard.png

为什么说这个实现不太优雅,这种实现是在inform中通知小明和小刘更新杂志的,但是这是经常变化的,可能又来几个人想订购杂志。我们要改动的地方有两处。

为该人员实现update方法,同时在inform中通知该人员。改动的地方多了,自然可能被遗忘,我们需要一个一劳永逸的方案。

优化

clipboard.png

inform中的代码是高度重复的,都是调用update并传this进去,并且这个订购杂志的人是经常变动的。所以我们需要建立一个订阅该杂志的一些人的集合。

List readers = new ArrayList();

然后我们在inform中就不用一行一行的写我要通知谁,直接遍历这个列表,都去调用其中的update

相信你已经发现了问题,因为这里没有泛型,所以从List中取出来的对象都是Object类型,我们怎么能保证其一定有update方法呢?怎么能保证这些update的参数都相同呢?所以我们需要一个声明update的接口。

/** * 接口:可更新 */public interface Updatable {    void update(Magazine magazine)}List
readers = new ArrayList
(); // 订购杂志的人员列表/** * 优化后的inform方法 */public void inform() { for (Updatable reader : readers) { reader.update(this); }}

优化之后,我们进行订阅变更时,无需修改inform方法,我们只需要去维护订购该杂志的人员列表就行了,减小了响应需求变更的成本。

观察者模式

如果你能理解我上面说的是什么,那么恭喜你。你已经明白了观察者模式。

都说观察者模式是在JDK实现中使用的最多的设计模式,这里姑且相信,毕竟,我也没有阅读过JDK的源码。

JDK中已经为我们提供了观察者模式的基础,一个可供订阅的类、一个描述交互消息的接口,这就是观察者模式。

JDK中可供订阅的基类:

package java.util;public class Observable {    private boolean changed = false;    private Vector
obs; public Observable() { obs = new Vector<>(); } public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) return; arrLocal = obs.toArray(); clearChanged(); } for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } public synchronized void deleteObservers() { obs.removeAllElements(); } protected synchronized void setChanged() { changed = true; } protected synchronized void clearChanged() { changed = false; } public synchronized boolean hasChanged() { return changed; } public synchronized int countObservers() { return obs.size(); }}

JDK中描述交互消息的接口:

package java.util;public interface Observer {    void update(Observable o, Object arg);}

实现代码

用于被订阅的杂志:

import java.util.Observable;/** * 当前杂志/最新杂志 */public class CurrentMagazine extends Observable {    private String name;                  // 当前杂志名称    public void setName(String name) {        this.name = name;        /**         * 这里设置改动的原因是在notifyObservers中         * 判断了是否有改动,如果改动为false,则终止         */        this.setChanged();                // 设置有变化        this.notifyObservers();           // 通知观察者    }    public String getName() {        return name;    }}

观察者,小明与小刘:

import java.util.Observable;import java.util.Observer;/** * 小明 */public class XiaoMing implements Observer {    @Override    public void update(Observable observable, Object arg) {        if (observable instanceof CurrentMagazine) {            CurrentMagazine currentMagazine = (CurrentMagazine) observable;            System.out.println("小明:" + currentMagazine.getName() + "出版了,我要去书店购买。");        }    }}import java.util.Observable;import java.util.Observer;/** * 小刘 */public class XiaoLiu implements Observer {    @Override    public void update(Observable observable, Object arg) {        if (observable instanceof CurrentMagazine) {            CurrentMagazine currentMagazine = (CurrentMagazine) observable;            System.out.println("小刘:" + currentMagazine.getName() + "出版了,我要去书店购买。");        }    }}

思考一下为什么这里需要判断当前Observable的类型呢?

小明和小刘可以订购多个消息,可以订购杂志,也可以订购天气预报。但是两者调用的都是update方法,所以要用instanceof判断究竟是我订阅的哪个服务推送的消息,然后再进行相应处理。

main方法:

import java.util.Observable;import java.util.Observer;public class Main {    public static void main(String[] args) {        Observable currentMagazine = new CurrentMagazine();     // 声明被订阅的杂志        Observer xiaoMing = new XiaoMing();                     // 声明观察者        Observer xiaoLiu = new XiaoLiu();        currentMagazine.addObserver(xiaoMing);                  // 观察者订阅杂志        currentMagazine.addObserver(xiaoLiu);        ((CurrentMagazine) currentMagazine).setName("电脑爱好者2018年第10期");        ((CurrentMagazine) currentMagazine).setName("电脑爱好者2018年第11期");    }}

声明杂志,声明观察者,让观察者订阅杂志,然后修改杂志信息,观察者会受到消息通知。

运行结果:

clipboard.png

一处代码细节

还记得代码整洁之道中有一条规范,就是我们的方法参数个数越少越好。参数越多,出错的可能性就会越大。

clipboard.png

这是Head First设计模式中的一处代码片段。第一眼看到这段代码,因为不符合我的风格嘛,自然多思考一下。

如果让我去实现这个display,我会为其设置参数。

public void display(float tempature, float humidity) {    System.out.println("Current Conditions: " + tempature + "F degrees and " + humidity + "% humidity");}

对比之下,还是感觉书上的实现好。在类的内部,消息沟通自然随意一些,直接使用内部变量即可沟通。而那些方法中的参数,都是本类不能自己提供,需要从外界获取的内容。如此,就减少了参数的个数,降低了交流的成本。

类内部,使用变量进行沟通,毕竟,封装的本质,就是减少各个操作之间数据的交流成本。以后多注意。

转载地址:http://bwell.baihongyu.com/

你可能感兴趣的文章
从初创型到独角兽企业,监控架构演进的那些事儿
查看>>
BAT集体升级云事业部,这背后都藏着哪些“小心思”?
查看>>
Oracle 裁员史:技术人死于重组,卒于云计算
查看>>
GNU parallel 笔记
查看>>
性能之巅:Linux网络性能分析工具
查看>>
InfoQ就Spring Boot 2.0 GA版发布采访了项目牵头人Phil Webb
查看>>
Oracle回应用户锁定,自治数据库是更好选择
查看>>
微软在C# 8中引入预览版可空引用类型
查看>>
度量和提高代码质量
查看>>
Go基础学习记录 - 编写Web应用程序 - 使用net/http包来构建Web应用
查看>>
斑马网络获超16亿元首轮融资,进入独角兽行列
查看>>
【攻克Redis】Redis基本知识
查看>>
wordpress修改域名
查看>>
资源整合贴
查看>>
Drill-on-YARN之部署
查看>>
致我们再也回不去的 Github ...
查看>>
zabbix监控进程存活
查看>>
Generator的正确打开方式
查看>>
机器学习实战-边学边读python代码(3)
查看>>
PostgreSQL jdbc driver的一个不完善之处
查看>>