设计模式三 观察者模式

来源:互联网 发布:国鑫商品交易软件 编辑:程序博客网 时间:2024/05/22 14:19

在众多的设计模式中有这样一个模式:

  • JDK中使用最多的模式之一;
  • Web工程师、Android工程师、Swing工程师日复一日、每天都在使用的模式之一;
  • 帮助我们的对象了解现况,不会错过该对象感兴趣的事情,对象在运行时还可以决定是否继续被通知;

这就是帅气如我的观察者模式.

那么观察者模式是一种什么样的模式?

  1. 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
  2. 观察者模式提供一种对象设计,让两个对象之间松耦合,但是他们依然可以交互。只是不太清楚彼此细节

上面说了太多官方话语,不易理解,还是继续让我们从一个现实实例去全方位的解析观察者模式的设计。

这里写图片描述

报纸,家家户户应该都看过,那这里如果我们了解下报纸的订阅是怎么回事,就知道了观察者模式是怎么回事了。
那现实中报纸是如何订阅呢?

  1. 报社的业务是出版报纸。
  2. 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到新报纸。
  3. 当你不想在看报纸的时候,取消订阅,他们就不会在送新报纸来。
  4. 只要报社还在运营,就会一直有人向他们订阅报纸或取消订阅报纸。

以上我们将报社改称为:“主题(Subject)”,订阅者改成为“观察者(Observer)”让我们再来看看他们之间如何运作的:
这里写图片描述

  • Subject为主题接口,对象使用此接口注册为观察者,或者把自己从观察者中删除
  • 每个主题可以有多个观察者
  • Observer为观察者接口
  • 所有的观察者必须实现观察者接口,这个接口只有update()一个方法,当主题状态改变时候调用update()
  • ConcreteSubject为一个具体主题,该类必须实现主题接口,除了注册和撤销方法之外,具体主题还实现了notifyObserver()方法,此方法用于在状态改变时通知所有当前观察者更新
  • ConcreteObserver为具体观察者。观察者必须注册具体主题,以便接受新的信息并更新

    关于观察者的一切,主题只知道观察者实现了Observer接口。主题不需要知道观察者的具体类是谁、做了些什么或其它任何细节。
    任何时候我们都可以注册新的观察者。因为主题唯一依赖的东西是一个现实了Observer接口的对象列表。所以我们可以随时注册观察者。
    改变主题或者观察者的其中一方,都不会影响到另外一方,因为两者是松耦合的。当有新观察者出现的时候,主题代码不需要修改;如果有新的类需要当观察者,我们不需要为了兼容新类型而修改主题代码。我们只需要将该类注册为观察者即可:
    如何注册一个观察者:

    1. 添加新类实现观察者接口
    2. 使用主题对象的注册方法即可

现在让我们用代码实现一个观察者模式Demo

我们先创建一个报纸对象,用来模拟出版设出版的新报纸:

public class Newspaper {    private String date;    private String title;    public Newspaper(String date, String title) {        super();        this.date = date;        this.title = title;    }    @Override    public String toString() {        return "Newspaper [date=" + date + ", title=" + title + "]";    }}

接着我们再从建立接口开始:

/** * 主题接口 */public interface Subject {    public void registerObserver(Observer o);//注册    public void removeObserver(Observer o);//取消注册    public void notifyObserver();//通知观察者}
/** * 观察者接口 */public interface Observer {    public void update(Newspaper newspaper);}
/** * 阅读最新报纸 */public interface Readable {    public void readNewspaper(Newspaper newspaper);//模拟看报纸}

然后我们实现一个具体的主题对象

/** *  具体的主题对象--zcuk报社 */public class ZuckPress implements Subject{    private List<Observer> observers;//用来保存当前已注册的观察者们    private Newspaper newspaper;//报社出版的最新报纸    public ZuckPress() {        observers = new ArrayList<Observer>();    }    public void setNewspaper(Newspaper newspaper) {        this.newspaper = newspaper;        notifyObserver();    }    /**     * 参数O用来向主题注册成为观察者(O订阅当前报社报纸)     */    @Override    public void registerObserver(Observer o) {        observers.add(o);    }    /**     * 参数O用来向主题取消注册(O取消订阅当前报社报纸)     */    @Override    public void removeObserver(Observer o) {        int i = observers.indexOf(o);        if(i>= 0){            observers.remove(i);        }    }    /**     * 当报社有新版报纸发布的时候,该方法被调用,以通知所有的观察者     */    @Override    public void notifyObserver() {        for(Observer o : observers){            o.update(newspaper);        }    }}

前面已经说过,主题对象是可以被多个观察者注册的,下面我们创建三个观察者,这三个观察者分别需要实现Observer接口(具有观察者的性质)和Readable接口(具有可以阅读新版报纸的性质)

/** * 观察者一 */public class AngleBaby implements Observer, Readable {    public AngleBaby(Subject concreteSubject) {        concreteSubject.registerObserver(this);    }    @Override    public void readNewspaper(Newspaper newspaper) {        System.out.println(getClass().getSimpleName() + "看到了新报纸, 报纸内容为:" + newspaper.toString());    }    @Override    public void update(Newspaper newspaper) {        readNewspaper(newspaper);    }}/** * 观察者二 */public class Jim implements Observer, Readable{    public Jim(Subject concreteSubject) {        concreteSubject.registerObserver(this);    }    @Override    public void update(Newspaper newspaper) {        readNewspaper(newspaper);    }    public void readNewspaper(Newspaper newspaper){        System.out.println(getClass().getSimpleName() + "看到了新报纸, 报纸内容为:" + newspaper.toString());    }}/** * 观察者三 */public class Tom implements Observer, Readable{    public Tom(Subject concreteSubject) {        concreteSubject.registerObserver(this);    }    @Override    public void update(Newspaper newspaper) {        readNewspaper(newspaper);    }    public void readNewspaper(Newspaper newspaper){        System.out.println(getClass().getSimpleName() + "看到了新报纸, 报纸内容为:" + newspaper.toString());    }}

最后来爽一把我们的观察者模式

    public static void main(String[] args) {        ZuckPress sub = new ZuckPress();//创建主题(报社)对象        //创建众多的观察者(订阅者)对象,这里将主题对象传给他们,以注册成为观察者;        Jim jim = new Jim(sub);        AngleBaby angleBaby = new AngleBaby(sub);        Tom tom = new Tom(sub);        System.out.println("模拟Zuck报社发布一版新的报纸\n");        sub.setNewspaper(new Newspaper("2015-03-20", "Magnum可实现Docker和OpenStack的完美集成"));        System.out.println("\n-------------------------华丽分割线-------------------------------\n");        //现在angleBaby和tom不再想订阅Zuck报社的报纸了        sub.removeObserver(angleBaby);        sub.removeObserver(tom);        System.out.println("angleBaby和tom已经取消订阅Zuck报社报纸,现在只有Jim是观察者后,我们再模拟发布一版新报纸\n");        sub.setNewspaper(new Newspaper("2015-03-21", "程序员讨厌没有价值的任务"));    }

控制台打印输出

Jim看到了新报纸, 报纸内容为:Newspaper [date=2015-03-20, title=Magnum可实现Docker和OpenStack的完美集成]AngleBaby看到了新报纸, 报纸内容为:Newspaper [date=2015-03-20, title=Magnum可实现Docker和OpenStack的完美集成]Tom看到了新报纸, 报纸内容为:Newspaper [date=2015-03-20, title=Magnum可实现Docker和OpenStack的完美集成]-------------------------华丽分割线-------------------------------Jim看到了新报纸, 报纸内容为:Newspaper [date=2015-03-21, title=程序员讨厌没有价值的任务]

我们首先将AngelBaby、Tom、Jim全部注册成为观察者,当Zuck报社发布新报纸后,根据控制台打印信息可以看到三位观察者已经全部看到了最新版的报纸
之后AngleBaby和Tom不再想订阅Zuck报社的报纸了。我们再模拟发布了一版新报纸,发现只有还没取消注册观察者的Jim收到了最新版的报纸。

下面是上述Demo的UML设计图
这里写图片描述

该图向我们展现了观察者模式的设计思路
在我们日常的代码中其实观察者模式也无处不在:

  • Android 项目开发中,我们经常使用ContentResolver来实现一个SQLite数据观察者。通过ContentResolver.notifyChange(Uri uri, ContentObserver observer)来发布数据发生变化的消息,让所用已使用ContentResolver.registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer);注册后的observer接受到相关发生变化的数据

  • Swing或者Android中当我们会看多许多的增加与删除Listener的方法,例如Android中我们为Button添加OnclickListener点击事件,这里Button即主题对象,OnclickListener即观察者,当我们的按钮按下也就是Button的状态发生变化的时候就会通知已经“订阅”过他的观察者OnclikListener。【之后OnclikListener会自行调用onClick方法,将当前的主题对象即button传递出去。(这里已不再是观察者模式的范畴。而是android开发当中用到恶心吐血的监听器模式)】

  • Android中的ListView组件中同样也存在观察者模式的踪影,我们可以回忆下,当ListView中列表数据发生变化后,我们一般都会调用 adapter.notifyDataSetChanged();通知ListView去刷新自己的列表。

  • 当然还有Web开发中经典的复合设计模式MVC中也包含了观察者模式…这个说起来又是一篇长篇大论啊…不想累赘了···大家自己去体会吧
0 0
原创粉丝点击