观察者模式

观察者模式


前言

定义
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

结构图 :
在这里插入图片描述

一、如何构建一个观察者模式?

观察者模式所涉及的角色有:

●  抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

●  具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

●  抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

●  具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

二、构建演示

构建一个 工作信息的主题,而求职者就是观察者,一旦有新的工作信息发布,就会推送给所有的求职者。

1.抽象主题

public interface Subject {//抽象主题
    //添加观察者
    void addObserver(Observer observer);

    //删除观察者
    void deleteObserver(Observer observer);

    //通知所有观察者
    void notifyObservers();
}

2.具体主题

这里的具体主题 是 工作信息发布通知

public class JobInfoSubject implements Subject{ //具体主题
    //一但发布新的工作信息,通知所有的求职者

    //新工作信息存储
    private String info;
    //集合,保存所有观察者
    private ArrayList<Observer> observers;
    public JobInfoSubject(){
        this.observers=new ArrayList<Observer>();
        this.info="";
    }

    @Override
    public void addObserver(Observer observer) { //添加观察者的具体方法
        if(observers.contains(observer))return;
        else if(observer!=null) observers.add(observer);
    }

    @Override
    public void deleteObserver(Observer observer) {//删除观察者的具体方法
        if(observer!=null&&observers.contains(observer))observers.remove(observer);
    }

    @Override
    public void notifyObservers() {//通知所有观察者
        for (Observer observer : observers) {
            //通知所有观察者--》通过观察者覆写的 具体接收方法
            observer.getInfo(info);
        }
    }

    //发布新的职位通知
    public void setInfo(String info) {
        if(info.length()>0&& !info.equals(this.info)){
            this.info = info;
            notifyObservers();//一但发布新职位,调用通知方法,通知所有观察者
        }

    }

}

3、抽象观察者

抽象观察者都具有一个 或者 n 个用来接收 具体主题通知的方法

public interface Observer {//观察者抽象

    //提供一个方法,用于接收具体主题的通知
    void getInfo(String msg);
}

4、具体观察者

求职者们

求职者们通过 含参构造,将实列保存到 具体主题的集合当中

public class Observer_A implements Observer{ //具体观察者
    Subject subject;
    String name;
    public Observer_A(Subject subject,String name){ //相当于注册信息
        this.subject=subject;
        subject.addObserver(this);
        this.name=name;
    }
    @Override
    public void getInfo(String msg) {
        System.out.println("你好 "+name+" 有新的职位发布:"+msg);
    }
}

5、实现

public class Main {
    public static void main(String[] args) {
        //获取具体主题
        JobInfoSubject jobInfoSubject = new JobInfoSubject();

        //求职者 注册  ---》通过 入参当中的 具体主题实列,调用主题的 addObserver方法将自己注册到 观察者 集合当中
        Observer_A a = new Observer_A(jobInfoSubject, "小明");
        Observer_A b = new Observer_A(jobInfoSubject, "小刘");
        Observer_A c = new Observer_A(jobInfoSubject, "小芬");

        //网页展示职位信息
        Observer_B showInfo = new Observer_B(jobInfoSubject);
        // 发布一条 职位信息
        jobInfoSubject.setInfo("东海龙宫招聘一只虾兵蟹将");
    }
}

运行结果

你好 小明 有新的职位发布:东海龙宫招聘一只虾兵蟹将
你好 小刘 有新的职位发布:东海龙宫招聘一只虾兵蟹将
你好 小芬 有新的职位发布:东海龙宫招聘一只虾兵蟹将
网页上展示新的求职信息 
 东海龙宫招聘一只虾兵蟹将

观察者模式的 推 和 拉 模型

具体主题在使用主题规定的方法通知具体接收者更新数据时会出现下列两种极端方式。
1.推数据方式
推数据方式是指具体主题将变化后的数据全部交给具体观察者,即将变化后的数据传递给具体观察者用于更新数据方法的参数。当具体主题认为具体观察者需要这些变换后的全部数据时往往采用推数据方式。

2.拉数据方式
拉数据方式是指具体主题不将变化后的数据交给具体观察者,而是提供了获得这些数据的方法,具体观察者在得到通知后,可以调用具体主题提供的方法得到数据(观察者自己把数据“拉”过来) ,但需要自己判断数据是否发生了变化。当具体主题不知道具体观察者是否需要这些变换后的数据时往往采用拉数据的方式。

列如上面的列子就是 推 的方式,如果想要它成为 拉 数据的方式,那么可以进行一下修改:

将数据保存再具体主题之中即可,提供一个 观察者主动拉去数据的方法,数据改变 只需要通知观察者。

public interface Subject_2 {//抽象主题
    //添加观察者
    void addObserver(Observer_2 Observer_2);

    //删除观察者
    void deleteObserver(Observer_2 Observer_2);

    //通知所有观察者
    void notifyObservers();

    //提供一个给 求职者主动拉取信息 的方法
    String getInfo();
}

增加 一个给观察者 主动拉去信息的方法,并只通知观察者 有新信息

public class JobInfoSubject_2 implements Subject_2 { //具体主题
    //一但发布新的工作信息,通知所有的求职者
    public boolean isChange=false;
    //新工作信息存储
    private String info;
    //集合,保存所有观察者
    private ArrayList<Observer_2> Observer_2s;
    
    public JobInfoSubject_2(){
        this.Observer_2s=new ArrayList<Observer_2>();
        this.info="";
    }

    @Override
    public void addObserver(Observer_2 Observer_2) { //添加观察者的具体方法
        if(Observer_2s.contains(Observer_2))return;
        else if(Observer_2!=null) Observer_2s.add(Observer_2);
    }

    @Override
    public void deleteObserver(Observer_2 Observer_2) {//删除观察者的具体方法
        if(Observer_2!=null&&Observer_2s.contains(Observer_2))Observer_2s.remove(Observer_2);
    }

    @Override
    public void notifyObservers() {//通知所有观察者
        for (Observer_2 Observer_2 : Observer_2s) {
            //通知所有观察者--》通过观察者覆写的 具体接收方法
            Observer_2.getInfo(isChange);
        }
        isChange=false;//通知完之后,将消息是否更新 变量 设置回 false
    }

    //发布新的职位通知
    public void setInfo(String info) {
        if(info.length()>0&& !info.equals(this.info)){
            this.info = info;
            this.isChange=true;
            notifyObservers();//一但发布新职位,调用通知方法,通知所有观察者
        }

    }
    
    //提供一个主动拉去 通知的方法
    @Override
    public String getInfo() {
        return info;
    }

}

抽象观察者

public interface Observer_2 {//观察者抽象

    //提供一个方法,用于接收具体主题的通知
    void getInfo(boolean change);
}

具体观察者

public class Observer_C implements Observer_2{ //具体观察者
    Subject_2 subject;
    String name;
    public Observer_C(Subject_2 subject, String name){ //相当于注册信息
        this.subject=subject;
        subject.addObserver(this);
        this.name=name;
    }
    @Override
    public void getInfo(boolean change) {
        if(change){
            System.out.println("有新职位发布");
        }
    }
    //主动拉去 职位信息
    public void show(){
        String info = subject.getInfo();
        System.out.println(info);
    }
}

实现

        //拉模式
        JobInfoSubject_2 jobInfoSubject_2 = new JobInfoSubject_2();
        Observer_C pull = new Observer_C(jobInfoSubject_2, "张三");
        //发布 新职位
        jobInfoSubject_2.setInfo("天庭招聘一位保安");

        //主动拉取信息
        pull.show();

运行结果

有新职位发布
天庭招聘以为保安

使用观察者模式的场景和优缺点

使用场景

关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优点

解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页