乐趣区

设计模式之观察者模式

定义

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式的 UML 类图及说明


如上图(图片来源于《head_first 设计模式》)所示,观察者的 uml 中主要有以下类
1. 主题 Subject(接口)
Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法,主要为观察者注册方法(registerObserver),观察者删除方法(removeObserver),通知观察者方法(notifyObservers)
2. 观察者 Observer(接口)
Observer 观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。主要提供一个 update 方法。
3. 具体主题 ConcreteSubject(Subject 的具体实现)
是对主题 Subject 接口的具体实现,该类可根据具体业务扩展
4. 具体观察者 ConcreteObserver(Observer 的具体实现)
是对 Observer 观察者的具体实现,一般订阅者会有多个,所以该类可通过自己需要的消息进行扩展

观察者模式的优缺点及应用场景

优点:
1. 观察者和被观察者是抽象耦合的。
2. 建立一套触发机制。
缺点:
1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
应用场景:
1. 关联行为场景
2. 事件多级触发场景
4. 跨系统的消息变换场景,如消息队列的处理机制
应用实例:
1. 新闻的发布订阅
2. 天气信息的发布订阅

注意事项:
1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

Spring 中的观察者模式

1. 事件(ApplicationEvent)
ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。

2. 事件监听(ApplicationListener)
ApplicationListener 事件监听器,也就是观察者。继承自 jdk 的 EventListener,该类中只有一个方法 onApplicationEvent。当监听的事件发生后该方法会被执行。

3. 事件发布(ApplicationContext)
ApplicationContext 是 Spring 中的核心容器,在事件监听中 ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自 ApplicationEventPublisher。在 ApplicationEventPublisher 中定义了事件发布的方法 — publishEvent(Object event)

4. 事件管理(ApplicationEventMulticaster)
ApplicationEventMulticaster 用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

观察者模式的实现

现在的线上直播模式非常的火,在这里,以简单的 LOL 比赛信息订阅为例。比如我们要关注今天有哪些了 LPL 的比赛信息,后几天有哪些比赛信息,我们就可以点击订阅,订阅后,有新的比赛信息,就会推送给你。

这样的场景下,我们就可以用观察者模式来实现:

定义主题接口

/**
 * 主题接口, 对象通过此接口注册为观察者,或者把自己从观察者中删除
 *
 * @author yyl
 */
public interface Subject {

    /**
     * 观察者注册
     *
     */
    void registerObserver(Observer observer);

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

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

定义主题接口的具体实现,及 LOL 比赛信息主题

/**
 * lol 主题
 *
 * @author yyl
 */
public class LolSubject implements Subject {

    /** 今日比赛信息 */
    private String msg;

    /** 后几日比赛信息 */
    private List<String> msgList;

    // 用户列表
    private static List<Observer> userList = new ArrayList<>();

    @Override
    public void registerObserver(Observer observer) {userList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {userList.remove(observer);
    }

    @Override
    public void notifyObservers() {userList.forEach(observer -> observer.update());
    }

    /**
     * 发布信息,并通知观察者
     *
     * @author yyl
     */
    public void setMsgs(String msg,List<String> msgList){
        // 更改通知信息
        this.msg = msg;
        this.msgList = msgList;
        // 通知观察者
        notifyObservers();}

    public String getMsg() {return msg;}

    public List<String> getMsgList() {return msgList;}
}

定义观察者 Observer

/**
 * 观察者 Observer,观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者
 *
 * @author yyl
 */
public interface Observer {
    /**
     * 接受消息后的具体逻辑处理
     */
    void update();}

具体观察者,今日比赛信息观察者

/**
 * 具体观察者,今日比赛信息观察者
 *
 * @author yyl
 */
public class CurrentObserver implements Observer {

    /**lol 比赛信息主题 */
    private LolSubject subject;

    /** 订阅的信息 */
    private String msg;

    public CurrentObserver(LolSubject subject) {
        this.subject = subject;
        // 观察者注册
        this.subject.registerObserver(this);
    }

    @Override
    public void update() {this.msg = this.subject.getMsg();
        System.out.println("今日比赛:" + this.msg);
    }
}

具体观察者,后几日比赛信息观察者

/**
 * 具体观察者,后几日比赛信息观察者
 *
 * @author yyl
 */
public class FutureObserver implements Observer {

    /**lol 比赛信息主题 */
    private LolSubject subject;

    /** 订阅的信息 */
    private List<String> msgList;

    public FutureObserver(LolSubject subject) {
        this.subject = subject;
        // 观察者注册
        this.subject.registerObserver(this);
    }

    @Override
    public void update() {this.msgList = this.subject.getMsgList();
        System.out.println("比赛预告:");
        msgList.forEach(s -> System.out.println(s));
    }
}

通知者进行通知 Client

public class TestObserver {public static void main(String[] args) {

        // 主题
        LolSubject lolSubject = new LolSubject();
        // 观察者
        CurrentObserver currentObserver = new CurrentObserver(lolSubject);
        FutureObserver futureObserver = new FutureObserver(lolSubject);

        List<String> msgList = new ArrayList<>();
        msgList.add("2020-06-22 RNG VS IG");
        msgList.add("2020-06-22 VG VS LGD");
        msgList.add("2020-06-22 V5 VS OMG");
        // 发布信息
        lolSubject.setMsgs("TSE VS FPX", msgList);
    }

}

如上代码,有两个观察者,在发布信息的比赛信息后,观察者就可以获取自己关注的信息,然后进行自己的业务逻辑处理。代码输出如下

有其他相关问题可以私聊咨询作者

退出移动版