更多关于设计模式的文章请点击:设计模式之禅(0)-目录页
观察者模式是Java标准库的开发中常用的一种设计模式,它完美的将观察者和被观察的对象分离开。当被观察者(主题)改变时,所有观察者(订阅者)都将受到改变的情况,而无论观察者处于什么样的状态。
一、什么是观察者模式
观察者模式(Observer Pattern
)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者设计模式定义了对象间的一种一对多的组合关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
二、通过设计气象站模块来说明观察者模式
2.1、观察者模式设计的一般类图
2.2、观察者模式带来的松耦合
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
为什么呢?
关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。
主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。
同样的,也可以在任何时候删除某些观察者。
有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
我们可以独立地复用主题或观察者。如果我们在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合。
2.3、气象站模块需求
2.4、使用观察者模式实现气象站模块
- Subject接口
/**
* @Description: 主题接口
* @CreateDate: Created in 2018/11/22 11:30
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface Subject {
//注册观察者
public void registerObserver(Observer observer);
//移除观察者
public void removeObserver(Observer observer);
//通知所有观察者
public void notifyObservers();
}
- Observer接口
/**
* @Description: 观察者的接口
* @CreateDate: Created in 2018/11/22 11:32
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface Observer {
//当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者。
public void update(float temp,float humidity,float pressure);
}
- DisplayElement类
/**
* @Description: DisplayElement接口只包含了一个方法,也就是display()。当布告板需要显示时,调用此方法。
* @CreateDate: Created in 2018/11/22 11:36
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface DisplayElement {
public void display();
}
- WeatherData类
/**
* @Description:
* @CreateDate: Created in 2018/11/22 11:38
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temp;
private float hudimity;
private float pressure;
public WeatherData(){
observers = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if(i >= 0){
observers.remove(observer);
}else{
System.out.println("can not delete--该主题没有被该观察者订阅!");
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temp,hudimity,pressure);
}
}
public void changed(){
notifyObservers();
}
public void setMeasurements(float t,float h,float p){
this.temp = t;
this.hudimity = h;
this.pressure = p;
changed();
}
}
- CurrentConditionDisplay类
/**
* @Description: 当前天气状况公告板
* @CreateDate: Created in 2018/11/22 11:48
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temp;
private float humidity;
private float pressure;
private Subject subject;
public CurrentConditionDisplay(Subject s){
this.subject = s;
//在该主题中注册该观察者,接受主题发布的变化
subject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
//更新完毕,输出更新后的状态
display();
}
@Override
public void display() {
System.out.println("当前天气状况:");
System.out.println("temp :"+temp+"\t humidity :"+humidity+"\t pressure :"+pressure);
}
}
- 测试气象站模块
/**
* @Description: 测试观察者模式构建的气象站能否使用
* @CreateDate: Created in 2018/11/22 11:55
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class TestWeatherData {
@Test
public void TestObserverPattern(){
WeatherData weatherData = new WeatherData();
//三个观察者去订阅weatherData主题,当主题有所改变,观察者会接受到改变
CurrentConditionDisplay conditionDisplay1 = new CurrentConditionDisplay(weatherData);
CurrentConditionDisplay conditionDisplay2 = new CurrentConditionDisplay(weatherData);
CurrentConditionDisplay conditionDisplay3 = new CurrentConditionDisplay(weatherData);
//主题移除掉一个观察者
weatherData.removeObserver(conditionDisplay1);
//更新数据
weatherData.setMeasurements(1.0f,2.0f,3.0f);
weatherData.setMeasurements(3.0f,2.0f,1.0f);
}
}