设计模式之观察者模式详解(Observer Pattern)
模式的定义与特点
定义
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
概念很清晰,我们举个例子来理解一下观察者模式的含义,我们都在新浪微博中关注过某一位明星(假设,当然很多人已经不玩微博了),每当这位明星发布一条动态时候,他的粉丝就都会知道。我们使用一张图来表示一下他们的关系。
上面这位明星在新浪微博上发了一条动态,说他会唱、跳rap等等。然后他的粉丝就都知道了。从这个例子中我们可以看到,这里包含了两种人,第一种是明星,第二个是粉丝。转化为设计模式中的语言就是主题和观察者。
我们的明星的微博就相当于与一个主题,粉丝就是观察者,随时观察明星的动态。不过明星有权利让你关注,也有权利把你拉黑。
特点
观察者模式是一种对象行为型模式,其主要优点如下。
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
它的主要缺点如下。
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
原理类图与角色说明
现在我们从类图的角度来看一下:
角色说明
-
Subject:抽象主题,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。意思就是明星把所有的粉丝都保存在一个账号里面,粉丝数量不限,可以新增粉丝也可以拉黑粉丝。
-
ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。意思是我们的明星一有动态,就会把消息给粉丝。
-
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。这就是我们所有粉丝的抽象。
-
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。具体每一个粉丝。
模式的应用场景
通过前面的分析与应用实例可知观察者模式适合以下几种情形。
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
应用实例
下面以天气状况推送为例子运用一下观察者模式:
定义抽象观察者(Observer):抽象的天气
//观察者接口,由具体观察者来实现
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
具体的观察者(concreteObserver):具体的天气状况使用者
//百度网站类,使用了推送来的天气信息
public class BaiduSite implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,这里使用推送模式
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
private void display() {
System.out.println("===百度网站====");
System.out.println("***百度网站 气温 : " + temperature + "***");
System.out.println("***百度网站 气压: " + pressure + "***");
System.out.println("***百度网站 湿度: " + humidity + "***");
}
}
//当前天气类,使用了推送来的天气信息
public class CurrentConditions implements Observer {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
// 更新 天气情况,是由 WeatherData 来调用,这里使用推送模式
@Override
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
private void display() {
System.out.println("===当前天气====");
System.out.println("***当前天气 气温 : " + temperature + "***");
System.out.println("***当前天气 气压: " + pressure + "***");
System.out.println("***当前天气 湿度: " + humidity + "***");
}
}
定义抽象主题(Subject):抽象天气信息站
//接口, 让 WeatherData 来实现
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
具体主题(ConcreteSubject):具体的天气信息站
/**
* 此类是核心
* 1. 包含最新的天气情况信息
* 2. 含有 观察者集合,使用 ArrayList 管理
* 3. 当数据有更新时,就主动的调用 ArrayList, 通知所有的(接入方)就看到最新的信息
*/
public class WeatherData implements Subject {
// 温度,气压,湿度
private float temperature;
private float pressure;
private float humidity;
List<Observer> observers;
public WeatherData() {
observers = new ArrayList<>();
}
public void setData(float temperature,float pressure,float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
//调用 dataChange, 将最新的信息 推送给 接入方 currentConditions
dataChange();
}
private void dataChange() {
//调用 接入方的 update
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this.temperature,this.pressure,this.humidity);
}
}
}
客户端调用测试
public class Client {
public static void main(String[] args) {
//创建一个 WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
BaiduSite baiduSite = new BaiduSite();
CurrentConditions currentCondition = new CurrentConditions();
// 注 册 到 weatherData
weatherData.registerObserver(baiduSite);
weatherData.registerObserver(currentCondition);
// 测 试
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(25f,88f,47f);
//移出当前天气的观察者,再次测试
weatherData.removeObserver(currentCondition);
System.out.println();
System.out.println("通知各个注册的观察者, 看看信息");
weatherData.setData(10f, 100f, 30.3f);
}
}
参考
23种设计模式之观察者模式
观察者模式(Observer模式)详解