观察者模式

观察者模式

前言

(本文总结于研磨设计模式,只摘录了要点,想要深入了解可以阅读研磨设计模式)


首先,假设我们要定义一个报纸定义系统,该系统存在两个实体,报社和订阅者。它们都有自己的诉求,报社希望能够在发报的时候清楚的知道自己的订阅者有哪些,而读者希望能够在报社发报的第一时间得到通知。 
进一步抽象的描述这个问题:当一个对象的状态发生改变的时候,如果让依赖于它的所有对象得到通知,并进行相应的处理。

解决方案:观察者模式

定义

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

个人理解:

目标对象与观察者之间是单向依赖,由于他们之间的依赖是动态的,而且又是接口依赖,所以耦合性很低。 
观察者需要什么讯息并不需要直接向目标对象传递什么数据。目标对象之所以知道需要区分观察者所需的数据,一方面可以采取不区分的办法,将所有数据,也就是目标对象自身传递给观察者。这种方式的好处在于面对多种需求的参数传递时,可以只定义一个观察者的update接口。而具体的参数选择则交给观察者来处理。另一方面,给update接口设置具体的参数,也明确的告诉了目标对象观察者所需的信息。这种方式一般用于信息传输的需求比较单一。但缺点就是失去了灵活性,扩展性差,如果有其他的需求就要重新定义接口了。 
所以,很多人纠结目标对象和观察者之间是如何联系起来的,观察者如果需求明确,就会给update方法设置一个具体的参数,比如String、int等等(不一定是基本类型,只是不是目标对象的类型或者其接口类型)。如果没有具体的需求就给update方法设置一个目标对象的类型或者其接口类型,这是目标对象能传递的最大数据集合,而具体要用什么就看观察者了。 
目标对象又是如何与观察者联系的呢?因为目标对象中维护着一个观察者对象的集合,遍历这些对象并调用其update方法可以执行定义在观察者中的方法内容了。

案例

目标对象:

   public class Subject {
    public List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void remove(Observer observer) {
        observers.remove(observer);
    }
    public void notifyObservers(){
        for (Observer observer : observers) {
            //没有参数,不要后后面的例子混淆,这里只是简单的写法,参数根据需求设置
            observer.update();
        }
    }
    }

观察者:

public class Observer {
    public void update() {
        System.out.println("内容改变了");
    }
}
进阶应用
  • 推模型: 
    目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,推送的信息通常是目标对象的全部数据,相当于是在广播通信。 
    例:
 private String content;
    public void notifyObservers(){
        for (Observer observer : observers) {
            //该方法定义在观察者中,参数的多少和类型由需求决定
            observer.update(content);
        }
    }
  • 拉模型: 
    目标对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到目标对象中获取,相当于观察者从目标对象中拉数据。一般这种模型的实现中,会把目标对象自身通过update方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个应用来获取了 
    例:
 public void notifyObservers(){
    for (Observer observer : observers) {
        //该方法定义在观察者中
        observer.update(this);
    }
}
  • 两种模式的对比: 
    1. 推模型是假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者需要什么数据,只有将自身传给观察者,让观察者自己按需取值。
    2. 推模型肯能会使观察者对象难以复用,因为观察者定义的update方法是按需定义的,可能无法兼顾考虑到使用情况。而拉模型是将自身传递给观察者,是目标对象能传递的最大数据集合了,基本上可以适应各种情况。
命名建议:
  • 观察者模式又被称为发布订阅模式。
  • 目标接口的定义,建议在名称后面跟Subject
  • 观察者接口的定义,建议在名称后面跟Observer
  • 观察者接口的更新方法,建议名称为update。
观察者模式的优缺点
  • 优点 
    1. 观察者模式实现了观察者和目标之间的抽象耦合
    2. 观察者模式实现了动态联动
    3. 观察者模式支持广播通信
  • 缺点 
    1. 可能引起无谓的操作
与其他模式的对比
  • 观察者模式和状态模式 
    观察者模式是当目标状态发生改变时,触发并通知观察者,让观察者去执行相应的操作。而状态模式是根据不同的状态,选择不同的实现,这个实现类的主要功能就是针对状态相应的操作,它不像观察者,观察者本身还有很多其他的功能,接受通知并执行相应的处理只是观察者的部分功能。 
    当然观察者模式和状态模式是可以结合使用的,观察者模式的重心在触发联动,但到底决定那先观察者会被联动,这时候可以采用状态模式来实现了,也可以采用状态模式来实现了,也可以采用策略模式来进行选择需要联动的观察者。 
    例:问题背景,现在要开发一个水质监测器,当水质正常时,值通知监测人员做记录,当为轻度污染是,除了通知监测人员外还要通知预警人员,判断是否需要预警。当为中度或高度污染是,除了通知监测人员外还要通知预警人员,判断是否需要预警,同时还要通知监测部门领导做相应的处理

    首先定义目标类,与上面不同的是将notifyObserver方法定义为抽象方法,交给子类实现:

    public abstract class Subject {
        protected List<Observer> observers =new ArrayList<>();
    
        public void attach(Observer observer) {
            observers.add(observer);
        }
    
        public void remove(Observer observer) {
            observers.remove(observer);
        }
        public abstract void notifyObservers();
    }

    在子类中结合状态模式实现notifyObservers

      public class WaterQuailty extends Subject {
    
        // 污染基本:0表示正常,1表示轻度污染,2表示中度污染,3表示重度污染
    
        private int polluteLevel=0;
    
    
     * 获取水质的污染级别
     * @return 水质污染级别
     */
    public int getPolluteLevel() {
        return polluteLevel;
    }
    
    /**
     * 当检查水质情况后,设置水质污染级别
     * @param polluteLevel
     */
    public void setPolluteLevel(int polluteLevel) {
        this.polluteLevel = polluteLevel;
    }
    
    @Override
    public void notifyObservers() {
        for (int i = 0; i < observers.size(); i++) {
             Watcher watcher= (Watcher) observers.get(i);
    
             if(this.polluteLevel>=0) {
                //通知监测人员
                 if ("监测人员".equals(watcher.getJob())) {
                     watcher.update(this);
                 }
             }
            if(this.polluteLevel>=1) {
                //通知预警人员
                if ("预警人员".equals(watcher.getJob())) {
                    watcher.update(this);
                }
            }
            if(this.polluteLevel>=2) {
                //通知监测部门领导
                if ("监测部门领导".equals(watcher.getJob())) {
                    watcher.update(this);
                }
            }
        }
    }
    }

    监听类的接口

    public interface Observer {
    
        public abstract void update(Subject subject);
    }

    监听类的实现类:

        public class Watcher implements Observer {
        public String getJob() {
            return job;
        }
    
        public void setJob(String job) {
            this.job = job;
        }
    
        private String job;
        @Override
        public void update(Subject subject) {
            WaterQuailty waterQuailty= (WaterQuailty) subject;
            System.out.println("内容改了"+waterQuailty.getPolluteLevel());
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值