本文从以下方法阐述观察者模式
一、定义观察者
二、观察者模式示例
三、推模型与拉模型
四、JAVA内置观察者
五、总结
一、定义观察者模式
1、定义
观察者模式为对象的行为模式,主要定义一种一对多的对象关系(观察者:主题对象=1:n)。让多个观察者对象去监听同一个主题对象Subject(被依赖的对象),当主题对象的内容状态发生变化时,会通知所有注册过的观察者对象,促使观察者对象的内容更新。
2、观察者模式的结构图
(1)以订报纸业务为例:
报社:Subject(主题类,被依赖的对象)
订阅者:Observer 观察者(依赖报社)
(2)报社Subject:负责,登记注册,通知,移除观察者的工作
(3)订阅者Observer:负责接收报社通知拿报纸
二、观察者模式示例
1、主题类接口
package net.oschina.design.observer.interfac;
/**
* 观察者模式中的主类,被依赖的类
*
* @author Freedom
*
*/
public interface Subject {
/*
* 注册观察者
*/
public void registObserver(Observer o);
// 移除观察者
public void removeObserver(Observer o);
// 通知观察者
public void notifyObserver();
}
2、观察者接口
package net.oschina.design.observer.interfac;
/**
* 观察者模式中 依赖的类
*
* @author Freedom
*
*/
public interface Observer {
/*
* 实时更新subject类中的数据
*/
public void updateDate(float temp, float press);
}
3、主题类的实现
package net.oschina.design.observer.interfac.impl;
import java.util.ArrayList;
import java.util.List;
import net.oschina.design.observer.interfac.Observer;
import net.oschina.design.observer.interfac.Subject;
/**
* 气象台主类
*
* @author Freedom
*
*/
public class WeatherData implements Subject {
private final List<Observer> observerList; // 模拟注册保存观察者数据的链表
private float temp;// 气温
private float press;// 气压
public WeatherData() {
observerList = new ArrayList<Observer>(5);
}
@Override
public void registObserver(Observer o) {
if (null != o) {
observerList.add(o);
}
}
@Override
public void removeObserver(Observer o) {
if (null != o) {
observerList.remove(o);
}
}
// 通知观察者
@Override
public void notifyObserver() {
if (null != observerList && !observerList.isEmpty()) {
for (Observer o : observerList) {
// 观察者实时显示天气信息
o.updateDate(this.getTemp(), this.getPress());
}
}
}
// 设置参数
public void setWeather(float temp, float press) {
this.temp = temp;
this.press = press;
// 气象台更新了数据后,通知观察者
this.notifyObserver();
}
/*
* setter /getter
*/
public float getTemp() {
return temp;
}
public void setTemp(float temp) {
this.temp = temp;
}
public float getPress() {
return press;
}
public void setPress(float press) {
this.press = press;
}
}
4、观察者类的实现
package net.oschina.design.observer.interfac.impl;
import net.oschina.design.observer.interfac.Observer;
/**
* 显示当前气温的观察者
*
* @author Freedom
*
*/
public class CurrentWeatherObserver implements Observer {
/*
* 观察者实时更新天气(non-Javadoc)
*
* @see
* net.oschina.design.observer.interfac.Observer#updateDate(net.oschina.
* design.observer.interfac.Subject)
*/
@Override
public void updateDate(float temp, float press) {
this.display(temp, press);
}
public void display(float temp, float press) {
System.out.println("今天天气****气温****" + temp);
System.out.println("今天天气****气压****" + press);
}
}
5、运行代码
package net.oschina.design.observer;
import net.oschina.design.observer.interfac.impl.CurrentWeatherObserver;
import net.oschina.design.observer.interfac.impl.WeatherData;
public class MainObserver {
public static void main(String[] args) {
// subject
WeatherData w = new WeatherData();
// observer
CurrentWeatherObserver cur = new CurrentWeatherObserver();
w.registObserver(cur);
w.setWeather(11.1f, 213.111111f);
}
}
上述代码运行分析:先实例化一个主题类对象,然后实例化一个观察者类的对象,之后主题对象中注册观察者对象。当主类对象中数据改变时,就通知观察者对象,观察者对象获取到主题对象发来的信息后,及时更新自己的信息并显示。
三、推模型与拉模型
在观察者模式中,又分为推模型和拉模型两种方式。
1、推模型
主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
2、拉模型
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
3、两种模型比较
(1)推模型是假定主题对象知道观察者需要的数据(个人愚见,主题的主动行为);而拉模型是主题对象不知道观察者具体需要什么数据,直接将主题对象本身传递给观察者(主题的被动行为所需数据要观察者根据需要自己获取),让观察者自己去按需要取值。
(2)推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能提供新的update()方法,或者是干脆重新实现观察者;而拉模型就不会造成这样的情况,因为拉模型下,update()方法的参数是主题对象本身,这基本上是主题对象能传递的最大数据集合了,基本上可以适应各种情况的需要。
四、JAVA内置观察者
1、主题类:继承Observable类
package net.oschina.design.observer.innerobserver.subject;
import java.util.Observable;
/**
* java内置观察者,主类
*
* @author Freedom
*
*/
public class WeatherData extends Observable {
private float temp;// 温度
private float press;// 气压
/*
* 主类数据变化通知观察者
*/
public void change() {
// 必须要调用setChanged方法
this.setChanged();
// 调用通知方法,调用通知方法,拉模型将主题的所有数据传递给观察者
this.notifyObservers(new Data(this.getTemp(), this.getPress()));
}
/**
* 设置主类中将要通知给观察者的数据
*/
public void setData(float temp, float press) {
this.temp = temp;
this.press = press;
// 调用通知方法
change();
}
public float getTemp() {
return temp;
}
public float getPress() {
return press;
}
/**
* 保存主类数据的内置对象
*
* @author Freedom
*
*/
public class Data {
private float temp;// 温度
private float press;// 气压
public Data(float temp, float press) {
this.temp = temp;
this.press = press;
}
public Data() {
}
public float getTemp() {
return temp;
}
public float getPress() {
return press;
}
}
}
2、观察者类实现java.util.Observer接口
package net.oschina.design.observer.innerobserver.observer;
import java.util.Observable;
import java.util.Observer;
import net.oschina.design.observer.innerobserver.subject.WeatherData.Data;
/**
* 当前天气的观察者
*
* @author Freedom
*
*/
public class CurrentWeather implements Observer {
private float temp;
private float press;
@Override
public void update(Observable o, Object arg) {
this.temp = ((Data) (arg)).getTemp();
this.press = ((Data) (arg)).getPress();
display();
}
// 显示天气
public void display() {
System.out.println("当天天气[温度]:" + temp + " [气压]:" + press);
}
}
3、运行类
package net.oschina.design.observer.innerobserver.main;
import net.oschina.design.observer.innerobserver.observer.CurrentWeather;
import net.oschina.design.observer.innerobserver.subject.WeatherData;
public class MainInnerObserver {
public static void main(String[] args) {
WeatherData w = new WeatherData();
CurrentWeather c = new CurrentWeather();
w.addObserver(c);// 注册观察者
w.setData(11.1f, 22.5f);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/a1fe83c1e816b211f2c47ded0b04a76d.png)
4、查看源码内置观察者的方法如下
5、注意事项:
①主类通知观察者对象之前要调用 setChanged()方法,然后再调用notifyObservers()通知方法;
解释:直接看源码一目了然
四、总结
1、观察者模式主要为了解决:对象之间存在1:N依赖关系的情况,被依赖的一方为主题类,依赖的一方为观察者类;
2、观察者模式又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式;
3、观察者模式,可以有效的降低对象之间的耦合度,比如说:当主题类停止工作时,观察者最多是接受不到数据,依然可以继续工作
4、java内置观察者,主题类为一个实体类Observable类而非接口,调用通知方法前先调用setChanged方法;内置观察者主题类中注册观察者时维护了一个集合为Vector
如上,内容可能还有遗漏欠缺,还请高端玩家斧正留言.....