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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://xuhong.blog.csdn.net/article/details/72124098

一、前言。


昨天我费尽心思了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.不管怎么样,按照自己的需求,选择哪种方式!反正都熟悉了观察者模式了。

展开阅读全文

没有更多推荐了,返回首页