java设计模式之观察者模式

前言

使用过springcloud的同学大概知道,不同的微服务都可以使用统一的配置中心,不同的微服务只需要将地址指向这个配置中心的IP及服务名就可以使用,后续一旦当配置中心的配置发生了某些变化,各个微服务模块都可以刷新并连接使用到最新的配置文件,不仅如此,许多的应用组件,比如zookeeper等,常用的带有监听功能的组件都有一种思想在里面,就是观察者模式的思想

观察者模式定义对象之间的一对多依赖,当一个对象状态发生改变时,它的所有依赖者都会收到通知并自动更新。 这样的好处就是可以大大降低两个或多个对象之间的耦合,依然可以交互,但不太清楚彼此的细节。观察者模式提供了一种对象的设计,让主题和观察者之间松耦合。松耦合的设计可以让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低

代码实现

在实际业务中,我们有这样一种场景,配置文件一般是从项目的配置资源目录下读取的,假如配配置文件存在一个公共的目录下,而其他的应用都需要从这个配置文件中读取配置信息,这样就是多个订阅者需要从某个源订阅需要的信息,当源文件的信息发生了变化,订阅者读取到的数据也发生响应的变化,下面就用代码来实现以下这个需求

在这里插入图片描述

现在假设有3个不同的微服务需要读取配置文件的数据库连接信息,可以定义为观察者,配置信息如下

dbName=test
userName=root
passWord=root

1、定义主题接口,所有的主题都实现主题接口

public interface Subject {

    //添加观察者
    public void addOberver(Observer observer);

    //删除指定的观察者
    public void deleteOberver(Observer observer);

    //通知观察者
    public void notifyObervers();

}

2、实现我们的主题类,也就是我们的被观察者

/**
 * 被观察者
 */
public class DbData implements Subject{

    //数据库名称
    private String dbName;

    //用户名
    private String userName;

    //密码
    private String passWord;

    //观察者列表
    private ArrayList<Observer> observerArrayList;

    public DbData(){
        this.observerArrayList = new ArrayList<Observer>();
    }

    /**
     * 添加指定的观察者对象
     * @param observer
     */
    @Override
    public void addOberver(Observer observer) {
        this.observerArrayList.add(observer);
    }

    /**
     * 删除指定的观察者对象
     * @param observer
     */
    @Override
    public void deleteOberver(Observer observer) {
        int i;
        if( (i = this.observerArrayList.indexOf(observer)) != -1){
            this.observerArrayList.remove(i);
        }
    }

    /**
     * 通知观察者
     */
    @Override
    public void notifyObervers() {
        for(Observer observer1 : this.observerArrayList){
            observer1.update(this.dbName, this.userName, this.passWord);
        }
    }

    /**
     * 被观察者的数据发生改变
     * @param dbName
     * @param userName
     * @param passWord
     */
    public void setMeasurements(String dbName, String userName, String passWord){
        this.dbName = dbName;
        this.userName = userName;
        this.passWord = passWord;
        this.measurementsChanged();
    }

    /**
     * 修改后,通知观察者
     */
    public void measurementsChanged() {
        this.notifyObervers();
    }

}

通过这个类,可以实现外部观察者的动态添加、移除的功能,当新加入了某个观察者后,一旦观察者订阅的某个业务数据发生了变化,该订阅者就可以被通知到

3、定义的观察者接口,所有观察者都实现此接口,即观察者对象

//定义观察者,所有的观察者都实现此接口
public interface Observer {

    /**
     * 调用观察者者更新接口
     * @param dbName
     * @param userName
     * @param passWord
     */
    public void update(String dbName, String userName, String passWord);

}

4、定义观察者要执行的公共接口,观察者要实现此接口来实现具体的业务逻辑

public interface DisplayElement {
    public void display();
}

5、定义观察者对象,在初始化时,注册主题(成为观察主题的对象)

第一个观察者:

/**
 * 第一个观察者
 */
public class Person1 implements Observer, DisplayElement {

    private String dbName;
    private String userName;
    private String passWord;

    private Subject subject;

    public Person1(Subject subject) {
        this.subject = subject;
        this.subject.addOberver(this);
    }

    //实际要做的业务
    @Override
    public void display() {
        System.out.println("person1: === >" + "数据库名称:" + this.dbName + "\t" + "用户名:" + this.userName + "\t" + "密码:" + this.passWord);
    }

    /**
     * 要更新数据的业务
     *
     * @param dbName
     * @param userName
     * @param passWord
     */
    @Override
    public void update(String dbName, String userName, String passWord) {
        this.dbName = dbName;
        this.userName = userName;
        this.passWord = passWord;
        this.display();
    }

}

第二个观察者:

/**
 * 第二个观察者
 */
public class Person2 implements Observer, DisplayElement {

    private String dbName;
    private String userName;
    private String passWord;
    private Subject subject;

    public Person2(Subject subject) {
        this.subject = subject;
        this.subject.addOberver(this);
    }

    //实际要做的业务
    @Override
    public void display() {
        System.out.println("person2: === >" + "数据库名称:" + this.dbName + "\t" + "用户名:" + this.userName + "\t" + "密码:" + this.passWord);
    }

    /**
     * 要更新数据的业务
     *
     * @param dbName
     * @param userName
     * @param passWord
     */
    @Override
    public void update(String dbName, String userName, String passWord) {
        this.dbName = dbName;
        this.userName = userName;
        this.passWord = passWord;
        this.display();
    }

}

第三个观察者:

/**
 * 第三个观察者
 */
public class Person3 implements Observer, DisplayElement {

    private String dbName;
    private String userName;
    private String passWord;
    private Subject subject;

    public Person3(Subject subject) {
        this.subject = subject;
        this.subject.addOberver(this);
    }

    //实际要做的业务
    @Override
    public void display() {
        System.out.println("person3: === >" + "数据库名称:" + this.dbName + "\t" + "用户名:" + this.userName + "\t" + "密码:" + this.passWord);
    }

    /**
     * 要更新数据的业务
     *
     * @param dbName
     * @param userName
     * @param passWord
     */
    @Override
    public void update(String dbName, String userName, String passWord) {
        this.dbName = dbName;
        this.userName = userName;
        this.passWord = passWord;
        this.display();
    }

}

读取数据库配置类:

public static DbVO getConfigDbProperties(){
        DbVO resVo = new DbVO();
        Properties properties = new Properties();
        InputStream inputStream = Object.class.getResourceAsStream("/db_config.properties");
        try {
            properties.load(inputStream);
            resVo.setDbName(properties.getProperty("dbName"));
            resVo.setUserName(properties.getProperty("userName"));
            resVo.setPassWord(properties.getProperty("passWord"));
            System.out.println(properties.getProperty("passWord"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return resVo;
    }

下面写一个测试类测试一下上面的代码:

public static void main(String[] args) {
        //被观察者
        DbData dbData = new DbData();

        Person1 person1 = new Person1(dbData);
        Person2 person2 = new Person2(dbData);
        Person3 person3 = new Person3(dbData);

        DbVO dbVO = PropertiesReaderUtils.getConfigDbProperties();
        dbData.setMeasurements(dbVO.getDbName(),dbVO.getUserName(), dbVO.getPassWord());

        System.out.println("现在剔除一个观察者");
        dbData.deleteOberver(person1);
        dbData.setMeasurements(dbVO.getDbName(),dbVO.getUserName(), dbVO.getPassWord());

    }

在这里插入图片描述

可以看到,各个观察者都可以成功监听到配置文件的数据,当剔除某个观察者的时候,该观察者就会从列表中剔除

方式2 java自带的observer

JDK也自带了观察者的主题类,我们也可以使用java自带的观察者实现上述的功能,只需要被观察者的对象继承Observable即可,其他的基本一样,下面直接上代码

被观察者对象

public class DbData2 extends Observable {

    //数据库名称
    private String dbName;

    //用户名
    private String userName;

    //密码
    private String passWord;

    public DbData2() {

    }

    /**
     * 被观察者数据发生改变
     * @param dbName
     * @param userName
     * @param passWord
     */
    public void setMeasurements(String dbName, String userName, String passWord) {
        this.dbName = dbName;
        this.userName = userName;
        this.passWord = passWord;
        this.measurementsChanged();
    }

    /**
     * 修改后,通知观察者
     */
    public void measurementsChanged() {
        super.setChanged();
        super.notifyObservers();
    }

    public String getDbName() {
        return dbName;
    }

    public String getUserName() {
        return userName;
    }

    public String getPassWord() {
        return passWord;
    }

}

定义3个观察者

import java.util.Observable;
import java.util.Observer;

public class Person11 implements Observer, DisplayElement {

    private String dbName;
    private String userName;
    private String passWord;

    private Observable observable;

    public Person11(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o instanceof DbData2) {
            this.dbName = ((DbData2) o).getDbName();
            this.userName = ((DbData2) o).getUserName();
            this.passWord = ((DbData2) o).getPassWord();
            this.display();
        }
    }

    @Override
    public void display() {
        System.out.println("person1: === >" + "数据库名称:" + this.dbName + "\t" + "用户名:" + this.userName + "\t" + "密码:" + this.passWord);
    }

}

其他的两个观察者和上面的一样,只需要修改一下名字即可,下面通过测试代码测试一下:

import com.sx.job.style.observer.DbVO;
import com.sx.job.style.observer.PropertiesReaderUtils;

public class Test2 {

    public static void main(String[] args) {
        //被观察者
        DbData2 weatherData = new DbData2();

        Person11 person11 = new Person11(weatherData);
        Person22 person22 = new Person22(weatherData);
        Person33 person33= new Person33(weatherData);

        DbVO dbVO = PropertiesReaderUtils.getConfigDbProperties();
        weatherData.setMeasurements(dbVO.getDbName(),dbVO.getUserName(), dbVO.getPassWord());

        System.out.println("现在剔除第一个观察者");
        weatherData.deleteObserver(person11);

        System.out.println("现在剔除第二个观察者");
        weatherData.deleteObserver(person22);

        weatherData.setMeasurements(dbVO.getDbName(),dbVO.getUserName(), dbVO.getPassWord());
    }

}

运行一下,观察效果如下,依然可以达到上述效果
在这里插入图片描述

本篇主要讲述了一下观察者模式的简单应用,更深入的大家可以继续研究,最后感谢观看!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小码农叔叔

谢谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值