前言
使用过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());
}
}
运行一下,观察效果如下,依然可以达到上述效果
本篇主要讲述了一下观察者模式的简单应用,更深入的大家可以继续研究,最后感谢观看!