观察者模式概述:
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
就像报纸和杂志的订阅:
1、报社的业务就是出版报纸,这相当于观察者模式里的主题,主题里的状态发生变化就相当于有了新闻,报社的任务是出
版报纸,而主题的任务就是发出变化的通知。
2、向某家报社订阅报纸,只要它们有新报纸出版,就会给你送来。只要你是它们的用户,你就会一直收到报纸。这相当于
观察者模式里的观察者,一旦主题发生变化并发出通知,那么所有依赖该主题(观察了该主题)的观察者都会收到通知。
3、当你不想再看报纸的时候,取消订阅,就不会再收到他们的报纸。这就相当于某个观察者取消观察了该主题,那么主题
再发生变化的时候发出变化的通知的时候该观察者就不会再收到通知。
4、只要报社还在运营,就会有人一直向他们订阅报纸或者取消订阅报纸。
观察者模式的结构:
有些新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有需要做的就是在新类里实现Observer接口,然后注册成为观察者就哦了。主题不在乎别的,它只会发送通知给所有实现了Observer接口并且注册了该主题的观察者。
主题接口:
<span style="font-size:14px;">package cn.wang;
public interface Subject {
void registObserver(Observer o);//注册到主题
void unregistObserver(Observer o);//取消注册
void notifyObservers();//通知所有观察者数据发生了变化
}</span>
观察者接口:
<span style="font-size:14px;">package cn.wang;
public interface Observer {
void update(String temp,String humidity,String pressure);
}</span>
具体的主题,实现主题接口:
<span style="font-size:14px;">package cn.wang;
import java.util.ArrayList;
import java.util.List;
public class WeatherDataSubject implements Subject {
private float temp;
private float humidity;
private float pressure;
private List<Observer> observers;
/**
* 主题的构造方法,我们在构造方法里维护一个Observer观察者集合
*/
public WeatherDataSubject() {
observers = new ArrayList<Observer>();
}
@Override
// 注册观察者
public void registObserver(Observer o) {
observers.add(o);
}
@Override
// 取消注册观察者
public void unregistObserver(Observer o) {
if (observers.contains(o)) {
observers.remove(o);
}
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
//这里将float的数据转换成String
o.update(String.valueOf(temp), String.valueOf(humidity), String.valueOf(pressure));
}
}
/**
* 气象台在数据变化的时候会调用该方法,不用管它是怎么被调用的,可能是传感器检测到温度变化了就会调用它
*/
private void dataSetChanged() {
// 数据发生了变化,就通知所有的观察者
notifyObservers();
}
public void setData(float temp, float humididy, float pressure) {
this.temp = temp;
this.humidity = humididy;
this.pressure = pressure;
// 数据发生变化调用dataSetChanged方法
dataSetChanged();
}
}</span>
温度数据的观察者:
<span style="font-size:14px;">package cn.wang;
public class TemperatureObserver implements Observer {
private Subject subject;//这里生成了主题的引用是为了以后可以取消注册。
/**
* 观察者实现了Observer接口之后要注册到主题,在这里我们在构造函数里进行注册
*/
public TemperatureObserver(Subject subject) {
//这里也可以注册其他的主题,只要相应的主题实现了Subject接口即可
this.subject = subject;
subject.registObserver(this);
}
@Override
public void update(String temp, String humidity, String pressure) {
// 这种方式是推的形式,每个观察者选择自己需要的数据来进行操作
System.out.println("当前温度是: " + temp);
}
}</span>
湿度数据的观察者:
<span style="font-size:14px;">package cn.wang;
public class HumityObserver implements Observer {
private Subject subject;
public HumityObserver(Subject subject) {
this.subject = subject;
subject.registObserver(this);
}
@Override
public void update(String temp, String humidity, String pressure) {
// 这种方式是推的形式,每个观察者选择自己需要的数据来进行操作
System.out.println("当前的湿度是: " + humidity);
}
}</span>
气压数据的观察者:
<span style="font-size:14px;">package cn.wang;
public class PressuerObserver implements Observer {
private Subject subject;
public PressuerObserver(Subject subject) {
this.subject = subject;
subject.registObserver(this);
}
@Override
public void update(String temp, String humidity, String pressure) {
// 这种方式是推的形式,每个观察者选择自己需要的数据来进行操作
System.out.println("当前的气压是: " + pressure);
}
}</span>
测试类:
<span style="font-size:14px;">package cn.wang;
public class Test {
public static void main(String[] args) {
// 实例化一个气象站
WeatherDataSubject s = new WeatherDataSubject();
// 实例化几个观察者
Observer tempObserver = new TemperatureObserver(s);
Observer humidityObserver = new HumityObserver(s);
Observer pressuerObserver = new PressuerObserver(s);
//改变主题的数据
s.setData(1.0f, 0.5f, 4.3f);
s.setData(2.0f, 0.6f, 5.2f);
}
}</span>
输出结果是:
<span style="font-size:14px;">当前温度是: 1.0
当前的湿度是: 0.5
当前的气压是: 4.3
当前温度是: 2.0
当前的湿度是: 0.6
当前的气压是: 5.2</span>
观察者模式松耦合:
我们可以独立地复用主题和观察者。如果我们需要在别的地方使用这些主题或者观察者,可以轻易的复用,因为这两者并不是紧耦合。
改变主题或观察者的其中一方,并不会影响另一方。因为两者是松耦合的,所以只要它们之间的接口仍被遵守,我们就可以自由滴改变它们。其实我们编码为了后期更好的可扩展和可维护性,应该为了交互对象之间的松耦合设计而努力。松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
Java API里的观察者模式:
Observable和Observer接口用起来很方便,因为许多功能都已经事先实现好了。你甚至可以用推或者拉的方式来获取变化的数据。
java API里观察者的类图:
java API里的主题,Observable:
java API里的观察者,Observer接口:
具体用法:
主题:
<span style="font-size:14px;">package com.wang;
import java.util.Observable;
public class WeatherDataSubject extends Observable {
private float temp;
private float humidity;
private float pressure;
/**
* 气象台在数据变化的时候会调用该方法,不用管它是怎么被调用的,可能是传感器检测到温度变化了就会调用它
*/
private void dataSetChanged() {
// 数据发生了变化,就通知所有的观察者
//在通知所有观察者之前要先调用这个方法
setChanged();
notifyObservers();//如果不传参,就是拉的形式
}
public void setData(float temp, float humididy, float pressure) {
this.temp = temp;
this.humidity = humididy;
this.pressure = pressure;
// 数据发生变化调用dataSetChanged方法
dataSetChanged();
}
//给各个属性设置一个getter方法,方便观察者拉数据
public float getTemp() {
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}</span>
温度观察者:
<span style="font-size:14px;">package com.wang;
import java.util.Observable;
import java.util.Observer;
public class TemperatureObserver implements Observer {
private Observable o;
/**
* 注册到Observable的具体类
* @param 被观察者,Observable的子类
*/
public TemperatureObserver(Observable o) {
this.o = o;
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherDataSubject) {
// 拉数据
System.out.println("当前的温度是: " + ((WeatherDataSubject) o).getTemp());
}
}
}</span>
湿度观察者:
<span style="font-size:14px;">package com.wang;
import java.util.Observable;
import java.util.Observer;
public class HumidityObserver implements Observer {
private Observable o;
public HumidityObserver(Observable o) {
this.o = o;
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherDataSubject){
System.out.println("当前的湿度是: "+((WeatherDataSubject)o).getHumidity());
}
}
}</span>
气压观察者:
<span style="font-size:14px;">package com.wang;
import java.util.Observable;
import java.util.Observer;
public class PressureObserver implements Observer {
private Observable o;
public PressureObserver(Observable o) {
this.o = o;
o.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherDataSubject){
System.out.println("当前的气压是: "+((WeatherDataSubject)o).getPressure());
}
}
}</span>
测试类:
<span style="font-size:14px;">package com.wang;
import java.util.Observer;
public class Test {
public static void main(String[] args) {
// 实例化一个主题
WeatherDataSubject subject = new WeatherDataSubject();
// 实例化温度观察者
Observer tempObserver = new TemperatureObserver(subject);
// 实例化湿度观察者
Observer humidityObserver = new HumidityObserver(subject);
// 实例化气压观察者
Observer pressureObserver = new PressureObserver(subject);
//改变主题的数据
subject.setData(1.0f, 0.5f, 2.3f);
System.out.println("-----------------------------");
subject.setData(1.2f, 0.8f, 3.2f);
}
}
</span>
结果:
<span style="font-size:14px;">当前的气压是: 2.3
当前的湿度是: 0.5
当前的温度是: 1.0
-----------------------------
当前的气压是: 3.2
当前的湿度是: 0.8
当前的温度是: 1.2</span>