Java基础再回首之设计模式系列②-----Observer 观察者模式(案列教程,附带demo)

来源:互联网 发布:顶尖条码电子称软件 编辑:程序博客网 时间:2024/06/03 22:55

一、前言。


昨天我费尽心思了4个小时,终于把我的设计模式系列的第一篇开了新的大门,有意者可以看看哒。
传送门:Java基础再回首之设计模式系列①—–StrategyPattern 策略者模式(案列教程,附带demo)

时间就像海绵,只要愿意挤,还是会有的。 ——-鲁迅

虽然身处工作的我,还是愿意挤出一点时间,充下电,今天给大家详细的图文并茂的分析 Observer 观察者模式,真的想一起学习哒~在博文下方留言,咱们一起学习,共勉设计模式带来的方便。


二、案列。


正是因为上篇文章的设计模式得到好评(哈哈~),又来了新的项目了,不过这次的项目要求有所不同。

1.为贵公司建立一个 新版本的电子杂志社的全新系统。 该电子杂志社必须建立JournalData对象中,由JournalData来负责追踪电子杂志社发来的各大新闻日报信息。

2.而且,该系统还是可以拓展的。可以作为API卖数据给第三方,这样可以建立良好的运营商业模式,一旦有客户上门,他们使用该服务数据都是需要收费的。


我们看看大概的思维导图:

1.JournalData对象作为一个中间站负责采集电子杂志社的各大新闻的信息。

2.只有订阅了对象系统的信息的客户,才可以收到对象系统的发来的公告。

3.各个客户各不知道彼此的信息订阅,如果某刻某个客户退出了订阅,不会影响到其他客户的订阅,而且其他客户也不知道,只有在对象系统中知道,而且从此之后,就不会再向该客户提供任何新闻信息。

这里写图片描述


三.代码实现。


我们看看代码架构:


这里写图片描述


这次我们不用普通方案,看了上面的要求。我开始先写3个接口:


①. ISubiect :分别作为 新增客户、移除客户、通知客户更新数据 ;

public interface ISubiect {    //注册一个观察者    void registerObserver(IObserver iObserver);    //移除一个观察者    void removeObserver(IObserver iObserver);    //通知有数据更新啦    void notifyObserver(IObserver iObserver);}

②. IObject : 当有新闻信息更新时后,系统会把一些新闻数据去传递给所有观察者。

public interface IObserver {    /**     *       * @param entertainmentNews 娱乐新闻     * @param militaryNews 军事新闻     * @param peopleNews 民生新闻     */    void update(String entertainmentNews,String  militaryNews,String peopleNews);}

③.IDisplayElement:此接口仅仅只是所有观察者要展示用户观察者收到的数据。(可以不用写,此处只是打印出收到的数据)

 public interface IDisplayElement {    //所有的观察者要展示自己受到的新闻信息    void DisplayElement();}

④我们看看杂志系统的代码:


public class JournalData implements ISubject {    //所有的观察者集合,并且泛型指向观察者接口    private List<IObserver> observer;    //娱乐新闻    private String entertainmentNews;    //军事新闻    private String militaryNews;    //民生新闻    private String peopleNews;    //构造方法就为集合实例化    public JournalData() {        observer = new ArrayList();    }    //注册一个观察者    @Override    public void registerObserver(IObserver iObserver) {        observer.add(iObserver);    }    //移除一个观察者    @Override    public void removeObserver(IObserver iObserver) {        int index = observer.indexOf(iObserver);        if (observer.size() > 0) {            observer.remove(index);        }    }    //通知所有的观察者有数据更新啦    @Override    public void notifyObserver() {        for (IObserver temIObserver : observer) {            temIObserver.update(entertainmentNews, militaryNews, peopleNews);        }    }    //设置数据    public void setJourData(String entertainmentNews, String militaryNews, String peopleNews) {        this.entertainmentNews = entertainmentNews;        this.militaryNews = militaryNews;        this.peopleNews = peopleNews;        notifyObserver();//别忘了设置完数据,通知所有观察者有数据更新啦     }}

⑤ 看看我们的用户怎么做的:

public class User1 implements IObserver, IDisplayElement {    //定义一个观察者管理的接口,方便以后可以移除操作    private ISubject iSubject;    private String entertainmentNews;    private String militaryNews;    private String peopleNews;    public User1(ISubject iSubject) {        this.iSubject = iSubject;        iSubject.registerObserver(this);    }    @Override    public void update(String entertainmentNews, String militaryNews, String peopleNews) {        this.entertainmentNews = entertainmentNews;        this.militaryNews = militaryNews;        this.peopleNews = peopleNews;        DisplayElement();    }    @Override    public void DisplayElement() {        System.out.print("用户一收到了娱乐新闻:" + entertainmentNews + ",军事新闻:" + militaryNews + ", 民生新闻:" + peopleNews);    }}

⑥、启动杂志社。


public class Main {    public static void main(String[] args) {        JournalData journalData = new JournalData();        //此处我使用匿名对象注册观察者        new User1(journalData);        new User2(journalData);        //有数据过来了        journalData.setJourData("明星黄晓明孩子出世啦"                , "美俄战机一周内黑海上空两度遭遇"                , "广州市民幸福度80%");    }}

四、总结以上观察者模式。

要点:

1.定义了对象之间的一对多的关系。

2.出版者用一个共同的接口来更新观察者数据更新。

3.出版者和观察者之间用松耦合的方式结合,出版者不知道观察者的细节,只知道了观察者实现了接口。

问题来了:

如果存在观察者想要自己动身去出版社取数据,而不是统一让出版社发出数据。毕竟有时候出版社已经有数据了,只是未到时间、参数不齐等其他原因,未能立刻发出去,但是已经有数据了的。这就造成了观察者心急但是吃不了热豆腐啊。那如何让观察者“心急也能吃到热豆腐呢?”,能立刻吃到“热豆腐呢?”。


五、利用Java内置的观察者模式。

很高兴告诉你,Java API有内置的观察者模式,Java.util包内包括最基本的Observer接口和Obersver类,这个和我们上面的ISubject接口和IObersver接口非常相似,利用内置的观察者模式更方便解决我所引出上面的问题。其功能都已经实现好了的,你在此作为出版社可以把信息放着,等待着观察者来取。当然啦!你也可以直接一次性推送出去给观察者。这样,你就可以免了搭建框架,又可以随心所欲的让观察者“心急也能吃到热豆腐!”。


5.1 内置的观察者模式源码分析。(先上完代码,再一步一步分析。)


1.现在我们继承要写的杂志系统要继承的 java.util包的Observable类。

public class JournalData extends Observable {    //娱乐新闻    private String entertainmentNews;    //军事新闻    private String militaryNews;    //民生新闻    private String peopleNews;    //设置数据    public void setJourData(String entertainmentNews, String militaryNews, String peopleNews) {        this.entertainmentNews = entertainmentNews;        this.militaryNews = militaryNews;        this.peopleNews = peopleNews;        dataChanged();    }    private void dataChanged() {        setChanged();        notifyObservers();    }    //就让取观察者自己来取“热豆腐”吧    public String getEntertainmentNews() {        return entertainmentNews;    }    public String getMilitaryNews() {        return militaryNews;    }    public String getPeopleNews() {        return peopleNews;    }}

2.看看我们的用户观察者类。


public class User1 implements Observer, Display {    private Observable observable;    //娱乐新闻    private String entertainmentNews;    //军事新闻    private String militaryNews;    //民生新闻    private String peopleNews;    //注册一个订阅    public void addObserver(Observable observable) {        this.observable = observable;        observable.addObserver(this);    }    //删除一个订阅    public void deleteObserver(Observable observable) {        observable.deleteObserver(this);    }    //删除所有订阅    public void deleteAllObserver() {        observable.deleteObservers();    }    @Override    public void update(Observable observable, Object arg) {      //先判断JournalData是否属于Observable的实例,如果存在多个Observable 子类,这行代码非常必要        if (observable instanceof JournalData) {            //如果是,把实例向下转型为JournalData对象            JournalData journalData = (JournalData) observable;            this.entertainmentNews = journalData.getEntertainmentNews();            this.militaryNews = journalData.getMilitaryNews();            this.peopleNews = journalData.getPeopleNews();      }    }    @Override    public void Display() {        System.out.print("用户一收到了娱乐新闻:" + entertainmentNews + ",军事新闻:" + militaryNews + ", 民生新闻:" + peopleNews);    }}

5.3 分析。

1.现在我们的杂志社改为了继承了一个类Obersver,说明一下,这个类不是抽象类!而是一个和我们上面手写的JournalData.java一样。都是有接口集合,看看右边的这个类的方法,发现它还有统计订阅者的个数方法 countObservers()。看看下图:


这里写图片描述


2.JournalData.java类里面有setChanged()、hasChanged、clearChanged()方法,旨在表示状态已经发生改变,做一个开关,如果出版社想要发送出去就调用父类的 clearChanged()方法,如果只是想让观察者自己来获取数据就setChanged()。总的来说,这就是一个开关。

官方这样解释的:想让推送者有自己的监控,可以随自己的要求去推送或者不推送,可以避免了最近注册的观察者错过了最近发出的通知。

下面我抽出官方源码来看看:(注意只有 changed=true才会推送出去,也就是调用setChanged()方法。)

 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);    }

3.现在我们把眼光转到用户的update()方法,可以看到有两个参数Observable observable和Object arg,第一个参数observable之前注册自己的被观察者,第二个参数作为可以传递对象过来的参数,一般地,如果不加以传递参数过来,都是为null,可以不加理会。


六、内置的观察者模式总结:

1.使用内置的Java观察者模式,可以轻松的实现“推”和“拉”的方式。可以按照推送者的要求去推送自己想要的内容。

2.内置的观察者模式通知观察者的次序是不一样的,实现了松耦合。

3.内置的观察者模式的黑暗面:它使用的是一个类,而不是一个接口,这大大限制了它的使用和复用,这违背了“多用组合,少用继承”的原则。

4.不管怎么样,按照自己的需求,选择哪种方式!反正都熟悉了观察者模式了。

0 0
原创粉丝点击