Java设计模式——观察者模式与Spring事件监听器

写在前面的话:终于通读了一遍《Spring源码深度解析》,在Spring的消息发布(Message)模块使用了设计模式中的观察者模式的经典实现,所以趁此机会总结一些。

1. 业务场景

Spring框架中的就是使用了观察者模式实现事件的消息发布(Message)模块的。

2. 传统处理/观察者模式

2.1 什么是观察者模式

这一次讲的情节很简单,但是充满了谋略和斗争。大体的意思就是三国初期,曹刘孙三家在徐州联手消灭了吕布,但是自己也伤了元气。而在此时袁术得了传国玉玺,在淮南称帝,兵精将广,图谋不轨,对三家威胁很大。于是曹刘孙三家在一起开会,决定派遣一名超级间谍打入袁术身旁,监视他的一举一动,这样的话一旦袁术想干什么坏事,他们就可以知道并做出相应调整,知己知彼百战百胜。

计是好计,问题时派谁去当这个超级间谍呢?正当大家愁眉苦脸的时候,美女貂蝉自告奋勇,想当年董卓那么厉害都让灭了,何况一个小小的袁术?大家一听有道理,于是就把貂蝉献给了元素当了妃子。另外三家还各自派出了小奸细常住在淮南城内,他们的任务就是当联络员,貂蝉有什么情报总不能自己曹刘孙三家挨个跑着送吧?直接丢给各国联络员,然后让他们通知各自的主公就OK了,而三家只要一接到各自奸细的通知,就会立即做出反应。

还有一个小插曲,袁术虽然手下了貂蝉,但是对她看管很严,大大方法的把情报送出去不可能,逼不得已,貂蝉设计了一套密码来传送情报,虽然密码方法没有公开,但是她想总有人可以破解吧,没错,对诸葛亮来说就是小菜一碟,从此袁术穿什么内裤曹刘孙三家都知道的清清楚楚。

用观察者模式来分析下上面的故事

  1. 美女貂蝉:貂蝉在观察者模式中叫做被观察者(Subject),主要任务是独立的管理后台数据和业务逻辑,同时尽可能不影响前台可会断界面变化的影响。当然,还要负责登记或者住校各个观察者。在这个故事里,貂蝉仅仅维护了一个数据,就是情报——私有变量info;另外还有一个业务逻辑,是用来加密info的方法Reverse(String str) 。每次得到新的情报,她就会先加密,然后找到在自己这登记过的联络员,让这些联络员联系自己的主公应变。情报一旦发出去,貂蝉的任务就算完成了,具体曹刘孙三家怎么应对,是打是降还是另有办法,那是三家自己的事情,和貂蝉没有什么关系了。
  2. 曹孙刘三家:曹孙刘三家在观察者模式中叫做观察者,主要任务就是从界面上用各种方式即时反映出被观察者,所谓“各种方式”就是说字符、图形、声音都可以表示同样的数据,只是外在表示不同而已,本质都是一些数据,所谓“即时”,就是说只要被观察者发生变化,观察者也会立即跟着变化,用行话叫做刷新Update吧,在这个故事里,我们可以看到运行结果中,每次貂蝉有什么新的情报,三家都会在屏幕上显示出来,虽然简单,但是他们确实是用各自不同的方式表示了同样的数据。

观察者模式又称为发布/订阅模式,在对象之间定义了一对多的依赖关系,这样依赖,当一个对象,依赖它的对象会收到通知并自动更新。

观察者模式类图

3. 代码实现

3.1 创建被观察者
public interface Subject<T> {

    /**
     * 订阅者列表
     */
    List<Observer> list = new ArrayList<Observer>();

    /**
     * 注册订阅者
     *
     * @param obs
     */
    void registerObserver(T obs);

    /**
     * 移除订阅者
     *
     * @param obs
     */
    void removeObserver(T obs);

    /**
     * 通知所有的观察者更新状态
     */
    void notifyAllObservers();

}
3.2 创建被观察者实现
public class CurrentSubject implements Subject<Observer> {

    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        // 被观察者值发生变化,请通知所有观察者
        this.notifyAllObservers();
    }

    /**
     * 注册订阅者
     *
     * @param obs
     */
    @Override
    public void registerObserver(Observer obs) {
        list.add(obs);
    }

    /**
     * 移除订阅者
     *
     * @param obs
     */
    @Override
    public void removeObserver(Observer obs) {
        list.remove(obs);
    }

    /**
     * 通知所有的观察者更新状态
     */
    @Override
    public void notifyAllObservers() {
        for (Observer observer : list) {
            // 更新每个观察者信息
            observer.update(this);
        }
    }
}

3.3 创建观察者
public interface Observer {
    /**
     * 修改方法
     */
    void update(Subject subject);
}
3.4 创建观察者实现
public class ObserverA implements Observer {

    /**
     * myState需要跟目标对象的state值保持一致!
     */
    private int myState;

    /**
     * 修改方法
     * 更新为和目标对象的值一致
     * @param subject
     */
    @Override
    public void update(Subject subject) {
        myState = ((CurrentSubject) subject).getState();
    }

    public int getMyState() {
        return myState;
    }

    public void setMyState(int myState) {
        this.myState = myState;
    }
3.5 测试方法
public class Test {
    public static void main(String[] args) {
        CurrentSubject currentSubject = new CurrentSubject();

        // 创建多个观察者
        ObserverA a = new ObserverA();
        ObserverA b = new ObserverA();
        ObserverA c = new ObserverA();

        // 将这三个观察者添加到subject对象的列表中
        currentSubject.registerObserver(a);
        currentSubject.registerObserver(b);
        currentSubject.registerObserver(c);

        // 改变subject的状态
        currentSubject.setState(3000);

        System.out.println("================第一次=================");
        System.out.println(a.getMyState());
        System.out.println(b.getMyState());
        System.out.println(c.getMyState());

        // 改变subject的状态
        currentSubject.setState(30);
        System.out.println("================第二次=================");
        System.out.println(a.getMyState());
        System.out.println(b.getMyState());
        System.out.println(c.getMyState());

        // 改变subject的状态
        currentSubject.setState(99);
        System.out.println("================第三次=================");
        System.out.println(a.getMyState());
        System.out.println(b.getMyState());
        System.out.println(c.getMyState());
    }
}

4. 观察者模式用途与优势

优点:解除观察者与被观察者之间的耦合。让耦合的双方都依赖抽象,而不是依赖具体。从而使得给子变化都不会影响另一边的变化。易于扩展,对于同一被观察者新增观察者无需修改原有代码。

缺点:依赖关系并未完全解除,抽象的被观察者仍然依赖观察者。使用观察者模式时需要开率开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般采用异步实现。可能会引起多余的数据通知。

用途:1. Spring框架的Message消息广播功能;
2. Servlet中监听器的实现;
3. JDK中内置了观察者模式经典实现;
4. 聊天室程序的,服务器转发给所有客户端。

5. 问题与解决方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值