观察者模式——案例分析和代码演示

1、什么是观察者模式:

先看几个案例

(1)案例:

       案例1:当前疫情严重,各个省区开学时间未定,但是各省区的开学时间要根据教育部的规定来制定,所以当教育部发布开学通知后,各省教育厅也会发布本省的开学计划。

      案例2:一些公司的假期安排和国家法定节假日的安排不一致,在这种情况下,员工在公司没有发布放假通知的时候,一般不会轻易的安排假期出行计划,当看到公司的放假安排后,公司内员工就会根据放假通知来安排假期生活。

      类似案例很多,有许多共性:

       多个对象(observer)的行为依赖于一个对象(subject)的行为,也即observer与subject存在多对一的关系,当subject对象修改了某些属性或者执行某个方法,总之就是subject发生了变化,则多个object也要进行相应的变化。

(2)概述

观察者模式定义了对象之间的多对一依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。此处,发生改变的对象称之为观察目标(Subject),而被通知的对象称之为观察者(Observer)。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。观察者模式又称为发布-订阅模式。

(3)模式结构

观察者模式一般需要四个角色:

抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,比如可以定义为ArrayList类型,通过add和remove来增加或者删除观察者对象。
具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有(可以通过遍历ArrayList元素的方式逐个通知)登记过的观察者发出通知。
抽象观察者(Observer):为所有的具体观察者定义一个接口(当观察者发现subject发生变化时需要作出的响应),在得到主题通知时更新自己。
具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

 

2、代码案例

(1)场景介绍

教育部公布了开学计划:剩余的假期的天数,开学后每周上课天数,然后安徽省和河南省教育厅看到教育部通知后,指定并发布本省的开学计划。

(2)代码

a、首先定义subject接口类Subject.java、至少包含注册、移除注册和通知所有观察者三个方法

package com.observe;
// subject接口,主题接口,里面定义了注册、删除和通知服务
public interface Subject {
    // 注册观察者(增加)
    public void registerObserver(Observer observer);

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

    // 当主题状态发生改变时,需要调用这个方法,以通知所有观察者
    public void notifyObserver();
}

b、定义定义observer接口类Observer.java,至少包含更新方法(当发现subject发生变化时自己需要执行的操作)

package com.observe;

// 观察者接口,定义的服务应该是看到被观察者subject发生变化后的反馈行动
public interface Observer {
    // 观察到subject变化后执行的操作
    public void changeBySubject(int schoolStartsDaysLeft, int studyDays);

}

c、具体的subject类MinistryOfEducation.java,需要实现Subject接口,在实际业务中,那些主动变化且变化后引起其他对象变化的对象定义为此类,此处把教育部定义为具体的subject类,因为教育部状态发生变化会引起各个省份的做出相应的变化。

package com.observe;

import java.util.ArrayList;
import java.util.List;

/**
 * @author wangql
 * @date 2020/4/5  22:38
 * @描述: MinistryOfEducation 是具体的被观察的对象,也即是多对一中的“一”所对应的对象
 */
public class MinistryOfEducation implements Subject{
        // 因为是多对一关系,所以此处的observers定义为list类型,通过add和remove来动态注册和注销
        private List<Observer> observers;
        private int schoolStartsDaysLeft;
        private int studyDays;

        public MinistryOfEducation(){
            observers = new ArrayList<Observer>();
        }
        
        // 覆盖接口中的通知方法
        @Override
        public void notifyObserver() {
            for(int i = 0; i < observers.size();i++){
                Observer observer = observers.get(i);
                observer.changeBySubject(schoolStartsDaysLeft, studyDays);
            }
        }

        // 覆盖接口中的注册方法
        @Override
        public void registerObserver(Observer observer) {
               observers.add(observer);
        }
        
        // 覆盖接口中的移除方法
        @Override
        public void removeOberver(Observer observer) {
               int i = observers.indexOf(observer);
               if(i >= 0){
                      observers.remove(i);
               }
         }
    
        // subject中所谓的变化(比如属性数据发生变化或者执行了某种操作),变化发生时执行通知方法
        public void change(int schoolStartsDaysLeft,int studyDays){
             this.schoolStartsDaysLeft = schoolStartsDaysLeft;
             this.studyDays = studyDays;
             // 上面是变化,下面是变化后触发的通知方法,此方法将会把观察者中相应的反馈方法都触发
             notifyObserver();
        }
}

d、本案例打算模拟安徽省和河南省两个省份作为观察者,此处定义第一个观察者AnHui.java,要实现Observer.java接口

package com.observe;

/**
 * @author wangql
 * @date 2020/4/5  22:44
 * @描述: 此类实现了Observer接口,也即是多对一中的“多”,是一个观察者
 */
public class AnHui implements Observer {
            private int schoolStartsDaysLeft;
            private int studyDays;
            private MinistryOfEducation ministryOfEducation;

            // 构造方法
            public AnHui(MinistryOfEducation ministryOfEducation){
                    this.ministryOfEducation = ministryOfEducation;
                    ministryOfEducation.registerObserver(this);       //注册观察者
             }

             // 接口Observer中的方法,检测到Subject变化后触发的服务
            public void changeBySubject(int  schoolStartsDaysLeft, int studyDays) {
                    this.schoolStartsDaysLeft = schoolStartsDaysLeft + 3;
                    this.studyDays = studyDays - 1;
                    System.out.println("AnHui's schoolStartsDaysLeft:"+this.schoolStartsDaysLeft
                            +"-----studyDays: "+this.studyDays);
             }

}

e、此处定义第一个观察者HeNan.java,要实现Observer.java接口

package com.observe;

/**
 * @author wangql
 * @date 2020/4/5  23:41
 * @描述
 */
public class HeNan implements Observer {
    private int schoolStartsDaysLeft;
    private int studyDays;
    private MinistryOfEducation ministryOfEducation;

    // 构造方法
    public HeNan(MinistryOfEducation ministryOfEducation){
        this.ministryOfEducation = ministryOfEducation;
        ministryOfEducation.registerObserver(this);      //注册观察者
    }

    // 接口Observer中的方法,检测到Subject变化后触发的服务
    public void changeBySubject(int  schoolStartsDaysLeft, int studyDays) {
        this.schoolStartsDaysLeft = schoolStartsDaysLeft + 4;
        this.studyDays = studyDays - 2;
        System.out.println("HeNan's schoolStartsDaysLeft:"+this.schoolStartsDaysLeft
                +"-----studyDays: "+this.studyDays);
    }
}

f、主类:

package com.observe;

/**
 * @author wangql
 * @date 2020/4/5  22:50
 * @描述
 */
public class MainTest {
    public static void main(String[] args) {
        // 创建一个Subject实例对象
        MinistryOfEducation ministryOfEducation = new MinistryOfEducation();

        // 创建Observer实例对象,调用了有参构造方法
        AnHui anHui = new AnHui(ministryOfEducation);
        HeNan heNan = new HeNan(ministryOfEducation);

        // subject具体对象发生变化的方法,此方法触发通知方法,
        //通知方法中遍历所有observer并执行每个observer中的changeBySubject方法,
        //达到subject改变后所有的observer都跟着变化的目的
        ministryOfEducation.change(21,5);
    }
}

3、总结:

(1)两个接口:Subject.java和Observer.java,其中Subject中要包含注册、移除注册和通知所有观察者三个方法,Observer要包含一个根据Subject的变化而变化的方法

(2)一个具体Subject实现类和多个具体的Observer实现类,Subject实现类实现了Subject接口,Observer实现类实现了Observer.java接口

(2)subject对象如何与observer建立关系的:这个关系是双向建立的

subject——>observer,通过subject中的ArrayList的add和remove,实现了注册和移除注册的功能。

observer——>subject,在observer中加入了Subject接口实现类的属性,然后在构造方法中调用了实现类的add注册方法,也即注册发生在新的observer产生的时候,产生的时候进行注册。本案例中,AnHui类中包含MinistryOfEducation类型的属性 ministryOfEducation,在AnHui类的构造方法中,执行ministryOfEducation的注册方法,把新建的AnHui类对象加入到subject对象中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值