美文网首页Java
设计模式之——观察者模式

设计模式之——观察者模式

作者: Jerry_1116 | 来源:发表于2019-03-12 23:39 被阅读0次

    1 观察者模式的定义

    观察者模式:Observer Pattern,也叫发布-订阅模式(Publish-Subscribe Pattern)。具体定义是:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
    观察者模式的通用类图:

    观察者模式通用类图
    • Subject被观察者
      定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。一般是抽象类或者接口,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
    • ConcreteSubject具体的被观察者
      定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
    • Observer观察者
      观察者接收到消息后,就进行update操作,对接收到的信息进行处理。
    • ConcreteObserver具体的观察者
      每个观察者在接收到消息后的处理应该是不同的,每个观察者有自己独特的处理逻辑。

    2 观察者模式通用代码示例

    1. 被观察者
    • 抽象的被观察者
    @Slf4j
    public abstract class Subject {
        /**
         * 定义一个观察者数组
         */
        private Vector<Observer> observers = new Vector<>();
    
        /**
         * 增加一个观察者
         *
         * @return true:增加成功;false:增加失败
         */
        public boolean addObserver(Observer observer) {
            return this.observers.add(observer);
        }
    
        /**
         * 删除一个观察者
         *
         * @return true:删除成功;false:删除失败
         */
        public boolean removeObserver(Observer observer) {
            return this.observers.remove(observer);
        }
    
        /**
         * 通知所有的观察者
         */
        public void notifyAllObservers() {
            log.info("{}通知所有的观察者.", this.getClass().getSimpleName());
            for (Observer observer : observers) {
                observer.update();
            }
        }
    }
    
    • 具体被观察者
    @Slf4j
    public class ConcreteSubject extends Subject {
    
        /**
         * 具体被观察者独特的业务逻辑
         */
        public void uniqueMethod() {
            //具体业务
            log.info("具体业务逻辑{}.uniqueMethod", this.getClass().getSimpleName());
            super.notifyAllObservers();
        }
    }
    
    1. 观察者
    • 观察者接口
      观察者一般是一个接口。
    public interface Observer {
        /**
         * 观察者的操作
         */
        public void update();
    }
    
    • 具体观察者
      每一个实现该接口的实现类都是具体观察者。
    @Slf4j
    public class ConcreteObserver implements Observer {
        /**
         * 实现具体的观察者接口,具有独特的业务
         */
        @Override
        public void update() {
            log.info("观察者{}接收到消息,正在进行处理...", this.getClass().getSimpleName());
        }
    }
    
    1. 场景类
    @Slf4j
    public class Client {
        public static void main(String[] args) {
            //创建一个被观察者
            ConcreteSubject subject = new ConcreteSubject();
            //定义一个观察者
            Observer observer = new ConcreteObserver();
            //观察者观察被观察者
            subject.addObserver(observer);
            //被观察者进行活动,观察者进行观察者
            subject.uniqueMethod();
        }
    }
    

    3 观察者模式的优缺点

    3.1 优点

    1. 观察者和被观察者之间是抽象耦合
      观察者和被观察者之间通过抽象耦合,这样设计在增加观察者或被观察者的时候,都非常容易扩展,不修改原有代码只需要增加具体实现就可以进行扩展,符合“开闭原则”。
    2. 建立一套触发机制
      观察者模式可以更方便地实现具体业务中的复杂逻辑关系,形成逻辑链条。

    3.2 缺点

    1. 开发效率低
      一个被观察者、多个观察者,开发和调试都会比较复杂,在大型项目中影响开发效率。
    2. 执行效率低
      在Java中消息的通知默认是顺序执行,一个观察者出错,会影响整体的执行效率,因此一般可以考虑采用异步方式。尤其是在多级触发的时候,执行效率更会受影响。
      因此在设计时注意考虑影响。

    4 观察者模式的应用

    4.1 应用场景

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

    4.2 使用观察者模式的注意事项

    使用观察者模式有两个重点问题需要解决。

    1. 广播链问题
      观察者模式中的观察者同时也可以是被观察者,因此很容易形成逻辑链。逻辑链一旦建立,逻辑就比较复杂,可维护性非常差。
      根据经验建议:
      在一个观察者模式中,最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),还是比较好控制的。

    广播链责任链模式最大的区别是:

    • 观察者广播链:消息在传播的过程中是随时更改的,它是由相邻的两个节点协商的消息结构。
    • 责任链模式:消息在传递的过程中基本上保持不变。如果要改变,也只有在原有的消息上进行修正。
    1. 异步处理问题
      如果被观察者发生动作了,观察者要做出响应。如果观察者比较多,就要用异步处理。异步处理就需要考虑线程安全和队列的问题,详情可以参考JDK的Message Queue。

    5 观察者模式的扩展

    5.1 Java中的观察者模式

    Java本身支持了观察者模式,并且提供了可用于扩展的父类:java.util.Observable实现类和java.util.Observer接口。

    Java中观察者模式通用类图
    1. 被观察者
      被观察者,继承java.util.Observable:
    @Slf4j
    public class ConcreteSubject extends Observable {
        /**
         * 具体被观察者独特的业务逻辑
         */
        public void uniqueMethod() {
            //具体业务
            log.info("具体业务逻辑{}.uniqueMethod", this.getClass().getSimpleName());
        }
    
        /**
         * 业务逻辑
         */
        public void doSomething() {
            log.info("被观察者{}执行业务逻辑...", this.getClass().getSimpleName());
            this.setChanged();
            this.notifyObservers();
        }
    }
    
    1. 观察者
      观察者实现java.util.Observer接口:
    @Slf4j
    public class ConcreteObserver implements Observer {
        /**
         * 观察者名字
         */
        private String name;
    
        public ConcreteObserver(String name) {
            this.name = name;
        }
    
        @Override
        public void update(Observable o, Object arg) {
            log.info("观察者[{}]观察到变化。{}.update", this.name, this.getClass().getSimpleName());
            ConcreteSubject observer= (ConcreteSubject) o;
            observer.uniqueMethod();
        }
    }
    
    1. 场景类
      场景应用观察者和被观察者
    @Slf4j
    public class Client {
        public static void main(String[] args) {
            ConcreteSubject subject = new ConcreteSubject();
            ConcreteObserver observer1 = new ConcreteObserver("OBSERVER1");
            ConcreteObserver observer2 = new ConcreteObserver("OBSERVER2");
            subject.addObserver(observer1);
            subject.addObserver(observer2);
    
            subject.doSomething();
        }
    }
    

    运行结果:

    23:04:10.055 [main] INFO com.idear.design.pattern.observer.jdk.ConcreteSubject - 被观察者ConcreteSubject执行业务逻辑...
    23:04:10.055 [main] INFO com.idear.design.pattern.observer.jdk.ConcreteObserver - 观察者[OBSERVER2]观察到变化。ConcreteObserver.update
    23:04:10.055 [main] INFO com.idear.design.pattern.observer.jdk.ConcreteSubject - 具体业务逻辑ConcreteSubject.uniqueMethod
    23:04:10.055 [main] INFO com.idear.design.pattern.observer.jdk.ConcreteObserver - 观察者[OBSERVER1]观察到变化。ConcreteObserver.update
    23:04:10.055 [main] INFO com.idear.design.pattern.observer.jdk.ConcreteSubject - 具体业务逻辑ConcreteSubject.uniqueMethod
    

    5.2 订阅-发布模型

    EJB3(Enterprise JavaBean)是一个非常优秀的框架,比起前两代,还算轻量级。EJB3中有3个类型的Bean:

    • Session Bean
    • Entity Bean
    • MessageDriven Bean。
      MessageDriven Bean(MDB),消息驱动Bean:消息的发布者(Provider)发布一个消息(一个消息驱动Bean),通过EJB容器(一般是Message Queue消息队列)通知订阅者做出相应。原理上很简单,类似于观察者模式的升级版。

    6 项目中真实的观察者模式

    一般在真实的项目中,系统设计时会对观察者模式进行改造或改装,主要体现在以下3个方面。

    1. 观察者和被观察者之间的消息沟通
      被观察者状态改变会触发观察者的一个行为,同时传递一个消息给观察者。在实际中一般做法是:观察者中的update方法接受两个参数,一个是被观察者,一个是DTO(Data Transfer Object,数据传输对象)。DTO一般是一个纯粹的JavaBean,由被观察者生成,由观察者消费。如果考虑到远程传输,可以通过JSON或XML格式传递。
    2. 观察者响应方式
      观察者一般逻辑复杂,需要接受被观察者传递过来的信息并进行处理。当一个观察者——多个被观察者时,性能就需要进行考虑。如果观察者来不及响应,被观察者执行时间也会被拉长。这种情况有两种处理办法:
    • 异步架构
      采用多线程技术,被观察者或者观察者都可以启动线程,可以明显地提高系统性能。
    • 同步架构
      采用缓存技术,保证有足够的资源,随时响应。但是,代价是开发难度大,需要进行充分的压力测试。
    1. 被观察者尽量自己做主
      被观察者具体实现根据业务逻辑决定是否要通知观察者,而不是消息到达观察者时才判断是否需要消费,导致浪费网络资源,加重消费者的处理逻辑和处理压力。

    参考

    1. 设计模式之禅

    相关文章

      网友评论

        本文标题:设计模式之——观察者模式

        本文链接:https://www.haomeiwen.com/subject/zwlmpqtx.html