观察者模式(Observer)

来源:互联网 发布:java sftp jcraft 编辑:程序博客网 时间:2024/06/10 21:54

观察者模式又称为发布订阅模式。一个发布者对应多个订阅者,一旦发布者的状态发生改变时,订阅者将收到订阅事件。本文中涉及的代码请点击这里。
先看看一个生活中的例子:

当我们想订一份报纸,我们先去邮局找到报纸的编号后填写订阅单并缴费。当报社有新报纸发出时,邮局会将我们订阅的报纸发给我们。

为了简单我们去掉邮局环节简化成:报社有新报纸后马上通知用户,这就是观察者。
定义对象间的一对多关系,当一个对象的状态发生变化时,所依赖于它的对象都得到通知并主动更新。在观察者模式中,多个订阅者成为观察者(Observer),被观察的对象成为目标(Subject)。观察者的UML模型如下:

观察者模式UML
观察者模式UML

先定义Subject并写一个ConcreteSubject继承Subject:

public class Subject {    private List<Observer> observers = new ArrayList<Observer>();    public void attach(Observer observer){        observers.add(observer);    }    public void detach(Observer observer){        observers.remove(observer);    }    public void notifyObservers(){        for (Observer observer : observers) {            observer.update(this);        }    }}public class ConcreteSubject extends Subject{    private String subjectState;    public String getSubjectState() {        return subjectState;    }    public void setSubjectState(String subjectState) {        this.subjectState = subjectState;        notifyObservers();    }}

再定义一个接口Observer,并写一个ConcreteObserver实现Observer接口:

interface Observer {    public void update(Subject subject);}public class ConcreteObserver implements Observer{    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public void update(Subject subject) {        System.out.println(name + "状态:" + ((ConcreteSubject)subject).getSubjectState());    }}

最后看看主函数方法:

public class Client2 {    public static void main(String[] args) {        ConcreteSubject subject = new ConcreteSubject();        ConcreteObserver observer1 = new ConcreteObserver();        observer1.setName("张三");        ConcreteObserver observer2 = new ConcreteObserver();        observer2.setName("李四");        ConcreteObserver observer3 = new ConcreteObserver();        observer3.setName("王二");        subject.attach(observer1);        subject.attach(observer2);        subject.attach(observer3);        subject.setSubjectState("看完报纸");    }}

打印出来的结果:

张三状态:看完报纸李四状态:看完报纸王二状态:看完报纸

在实现观察者模式的时候,一定要注意触发通知的时机。一般情况下是在完成了状态改变之后触发,因为通知会传递数据,比如在setSubjectState时先通知观测者就会发生错误

// 错误写法public void setSubjectState(String subjectState) {    notifyObservers();  // 通知应该放在状态改变之后,因为 update(Subject subject) 中的参数类型为Subject     this.subjectState = subjectState;}

在观察者模式的实现上,有推模式和拉模式两种方式:

  • 推模式
    Subject主动向Observer推送消息,不管对方是否需要,推送的信息通常是目标对象的全部或部分数据,相当于广播通信。
  • 拉模型
    Subject在通知Observer时只传递少量信息,如果观察者需要更具体的信息,再由Observer主动去拉取数据。这样的模型实现中会把Subject自身通过update方法传入到Observer。

当前上面的实现使用的就是拉模型。通过(ConcreteSubject)subject得到具体对象,获得信息。

当然Java本身就有观察者模式的部分实现,分别是java.util.Observable java.util.Observable
下面看一个使用Java自带观察者模式的例子:

新的目标直接继承Java中定义的Observerable:

public class NewsPaperObservable extends Observable{    private String content;    public String getContent() {        return content;    }    public void setContent(String content) {        this.content = content;        setChanged();  // 必须调用这个方法来通知Observer状态发生了改变        notifyObservers(content);    }}

新的观察者也直接实现Observer接口:

public class ReaderObserver implements Observer{    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public void update(Observable o, Object arg) {        System.out.println(name + "读取推送信息" + arg);        System.out.println(name + "读取拉取信息" + ((NewsPaperObservable)o).getContent());    }}

主函数和前面的相似:

public class Client {    public static void main(String[] args) {        NewsPaperObservable subject = new NewsPaperObservable();        ReaderObserver reader1 = new ReaderObserver();        reader1.setName("张三");        ReaderObserver reader2 = new ReaderObserver();        reader2.setName("李四");        ReaderObserver reader3 = new ReaderObserver();        reader3.setName("王二");        subject.addObserver(reader1);        subject.addObserver(reader2);        subject.addObserver(reader3);        subject.setContent("粗大事啦啦啦");    }}

打印出结果:

王二读取推送信息粗大事啦啦啦王二读取拉取信息粗大事啦啦啦李四读取推送信息粗大事啦啦啦李四读取拉取信息粗大事啦啦啦张三读取推送信息粗大事啦啦啦张三读取拉取信息粗大事啦啦啦

使用Java自带的观察者模式需要注意以下几个问题:

  • 具体的目标实现中不需要再维护观察者的注册信息了,这个在Java中的Observable类中实现了。
  • 触发通知方式有些变化,需要先调用setChanged方法。
  • 具体的观察者中,update()方法其实能同时支持推模型和拉模型。

源码:https://github.com/jianjianH/Design-Patterns

原文地址:http://www.jianshu.com/p/eee10a55112f

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 柿子和螃蟹后要怎么办 柿子和螃蟹吃了怎么办 吃了没熟的虾怎么办 邻居小孩怕我家小狗怎么办 心里有一道坎过不去了怎么办 刚买的小狗怕人怎么办 一年级孩子字写不好怎么办 小狗三天没吃了怎么办 捡到一只流浪猫怎么办 仓鼠四肢红肿圈状怎么办 泰迪的鼻子干燥怎么办 小狗眼睛有白色浓稠物怎么办 流浪狗生了小狗怎么办 学生字写得很差怎么办 猫身上粘老鼠胶怎么办 抄东西抄的手疼怎么办 皮质物品被油性笔划了怎么办 在小区猫丢了怎么办 母猫把小猫丢了怎么办 小狗不吃东西没精神怎么办 小狗的鼻子烂了怎么办 狗老是在家拉尿怎么办 狗狗鼻子有点干怎么办 狗的鼻头不黑了怎么办 金毛鼻头不黑怎么办 金毛毛掉了不长怎么办 狗狗鼻子烂了怎么办 小比熊鼻子不黑怎么办 狗狗鼻子起皮怎么办 金鱼身子弯了是怎么办 属狗的纹龙怎么办 卫生间的墙空的怎么办 花生苗长得好怎么办 菊花上面的白虫怎么办 小狗不吃东西还吐怎么办 小狗呕吐不吃东西没精神怎么办 小狗生病了不吃东西怎么办 小兔子腿摔了怎么办 刺猬葡萄我们骄傲我们该怎么办 小狗被邻居家大狗咬死了怎么办 狗狗死胎在腹中怎么办