观察者模式(Observer Pattern)_Java
Observer Pattern
问题:
有一个气象站(WeatherData),用来给各种手机(苹果,三星,摩托罗拉等等)提供气象信息,要求是,当气象信息更新的时候,给各个手机发送新的天气信息。
方案一
//Iphone类
package com.pattern.observer;
/**
* 苹果手机
*/
public class Iphone {
/**
* 苹果手机用来显示天气的方法
*/
public void display(float low,float height,String weather){
System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
}
}
//Android类
package com.pattern.observer;
/**
* 安卓手机
*/
public class Android {
/**
* 安卓手机用来显示天气的方法
*/
public void display(float low,float height,String weather){
System.out.println("Android:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
}
}
//WeatherData类
package com.pattern.observer;
/**
* 用来给手机厂商提供天气信息的公共类
*/
public class WeatherData {
private float low;//最低气温
private float height;//最高气温
private String weather;//天气情况
/**
* 提供给气象人员用来更新天气信息的,当天气更新时,掉用changed方法
*/
public void setData(float low,float height,String weather){
this.low=low;
this.height=height;
this.weather=weather;
changed();
}
/**
* changed方法,用来给接入的手机发送天气信息
*/
private void changed() {
//苹果接入,给苹果手机提供天气服务
Iphone iphone=new Iphone();
iphone.display(getLow(), getHeight(), getWeather());
//安卓接入,给安卓手机提供天气服务
Android android=new Android();
android.display(getLow(), getHeight(), getWeather());
}
public float getLow() {
return low;
}
public float getHeight() {
return height;
}
public String getWeather() {
return weather;
}
}
//Test类
package com.pattern.observer;
public class Test {
public static void main(String[] args) {
WeatherData wd=new WeatherData();
//天气人员更新天气,手机显示更新的天气
wd.setData(32, 41, "晴天");
}
}
方案一的问题
如果不断有手机厂商需要接入我们的天气服务,或者不断有手机厂商不需要我们的天气服务了,我们就得不停的改动WeatherData类,这是很麻烦的事情。
问题的解决
拿出版社来举例说明
- 出版社的任务就是出版报纸
- 你订阅了报纸,出版社就给你送
- 你不需要该报纸了,你可以退订
- 气象站一直提供气象服务
- 气象站提供一个定制服务
- 你定制了,就给你送,你取消定制了,就不给你送了
观察者模式
- 定义了对象间 一对多 的依赖
- 报社→一
- 订阅者→多
- 订阅者是观察者的对象
方案二
//Subject接口
package com.pattern.observer1;
/**
*提供订阅服务的接口
*/
public interface Subject {
public void registerObserver(Observer observer);//注册观察者
public void removeObserver(Observer observer);//删除观察者
public void notifyObserver();//通知观察者
}
//Observer接口
package com.pattern.observer1;
/**
* 观察者接口,实现此接口后方可去订阅主题,天气主题的观察者需要提供显示天气的方法
*/
public interface Observer {
//观察者必须实现此方法,用来显示天气
public void display(float low,float height,String weather);
}
//WeatherData类
package com.pattern.observer1;
import java.util.ArrayList;
import java.util.List;
/**
* 天气数据提供订阅,实现主题接口
*/
public class WeatherData implements Subject{
private float low;
private float height;
private String weather;
private List<Observer> args=new ArrayList<Observer>();//当前气象站具有的观察者集合
public float getLow() {
return low;
}
public float getHeight() {
return height;
}
public String getWeather() {
return weather;
}
public void setData(float low,float height,String weather){
this.low=low;
this.height=height;
this.weather=weather;
changed();
}
private void changed() {
notifyObserver();
}
/**
* 注册观察者,如果集合中不存在,则添加进集合
*/
@Override
public void registerObserver(Observer observer) {
if(!args.contains(observer)){
args.add(observer);
}
}
/**
* 删除观察者,若存在此观察者,删除此观察者
*/
@Override
public void removeObserver(Observer observer) {
if(args.contains(observer)){
args.remove(observer);
}
}
/**
* 数据改变时,调用此方法,给每一个观察者发送改变后的信息
*/
@Override
public void notifyObserver() {
for (Observer o : args) {
o.display(getLow(), getHeight(), getWeather());
}
}
}
//Iphone类
package com.pattern.observer1;
/**
* Iphone实现观察者接口,是一名观察者,是否订阅天气,看采用哪一种构造方法
*/
public class Iphone implements Observer{
public Iphone(){
}
/**
* 注册天气信息,在气象站注册信息,this指当前对象
*/
public Iphone(Subject sub){
sub.registerObserver(this);
}
/**
* 当成为气象站的观察者后,天气发生改变后,会收到通知,调用此方法
*/
@Override
public void display(float low, float height, String weather) {
System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
}
}
//Test类
package com.pattern.observer1;
public class Test {
@SuppressWarnings("unused")
public static void main(String[] args) {
WeatherData wd=new WeatherData();
Iphone i=new Iphone(wd);//i手机注册天气服务
Iphone i2=new Iphone(wd);//i2手机也注册天气服务
wd.removeObserver(i);//气象站取消了i手机的订阅
wd.setData(23, 25, "windy");//这里只给i2提供天气服务
}
}
方案三
Java内置的观察者模式(Observable-Observer)
//Observable类
package com.pattern.observer2;
import java.util.Vector;
@SuppressWarnings("unchecked")
public class Observable {
private boolean changed = false;//观察者所观察的信息是否改变
@SuppressWarnings("rawtypes")
private Vector obs;//用来存放观察者的集合
/**
* 构造一个没有观察者的Observable对象
*/
@SuppressWarnings("rawtypes")
public Observable() {
obs = new Vector();
}
/**
* 线程安全模式下,添加一个观察者
* 如果观察者为null,则抛异常
* 如果当前观察者列表中没有此观察者,则添加进来
*/
public synchronized void addObserver(Observer o) {
if (o == null){
throw new NullPointerException();
}
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* 删除一个观察者,如果删除的不存在,也没有影响
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/**
* 这个方法是notifyObservers方法的重载,用来向所有观察者提供信息的
*/
public void notifyObservers() {
notifyObservers(null);//调用下面的方法,传入参数null
}
/**
* 如果Observable的状态发生了改变,则通知所有观察者
*/
public void notifyObservers(Object arg) {
Object[] arrLocal;//临时数组
synchronized (this) {
if (!changed){
return;//如果没有改变,则什么也不做
}
arrLocal = obs.toArray();//若改变了将obs集合转成数组
clearChanged();//设置changed为false
}
//给每一个观察者送出通知
for (int i = arrLocal.length-1; i>=0; i--){
((Observer)arrLocal[i]).update(this, arg);
}
}
/**
* 删除所有的观察者
*/
public synchronized void deleteObservers() {
obs.removeAllElements();
}
/**
* 用来设置Observable对象发生了改变
*/
protected synchronized void setChanged() {
changed = true;
}
/**
* 所有的观察者都接收到通知后,再把Observable对象设置为未改变
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* 判断Observable对象是否改变,返回changed的值
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* 统计当前Observale对象中有多少观察者
*/
public synchronized int countObservers() {
return obs.size();
}
}
//Observer接口
package com.pattern.observer2;
/**
* 所有的观察者都要实现观察者接口
*/
public interface Observer {
/**
* 所有的观察者都要实现update方法,用来接收被观察对象传来的信息
*/
void update(Observable o, Object arg);
}
//WeatherData类
package com.pattern.observer2;
import java.util.Observable;
public class WeatherData extends Observable{
private float low;
private float height;
private String weather;
public float getLow() {
return low;
}
public float getHeight() {
return height;
}
public String getWeather() {
return weather;
}
/**
* 更新天气,调用changed方法,在changed方法中调用setChanged,notifyObservers
*/
public void setData(float low,float height,String weather){
this.low=low;
this.height=height;
this.weather=weather;
changed();
}
/**
* 此方法可有可无,可以将内容写到setData中,无所谓的
*/
private void changed() {
setChanged();
notifyObservers();
/**
* notifyObservers(Object arg)这个方法的作用是啥呢?
* 使用notifyObservers()是拉模式,给用户提供获取天气信息的接口,如果需要,通过接口来拿即可
* 带参数的方法,可以用来实现气象站主动给你送天气,你直接用即可
* 例:
* notifyObservers(new Object[]{getLow(),getHeight(),getWeather()}
* Iphone中的update方法中的ars包含了此数组
* 直接取即可
* args[0]
* args[1]
* args[2]
*
*/
}
}
//Iphone类
package com.pattern.observer2;
import java.util.Observable;
import java.util.Observer;
public class Iphone implements Observer{
public Iphone(){
}
/**
* 用于注册到一个Observable的观察者
* 如果一个Iphone对象想接收某一个天气站的天气信息,就必须先注册人家的服务
*/
public Iphone(Observable sub){
sub.addObserver(this);
}
/**
* 重写update方法
* 如果当前对象注册了,并且信息发生了改变,就给当前对象送来Observable对象o,o中包含有所有的天气信息
* 从o中提起信息即可
*/
@Override
public void update(Observable o, Object arg) {
WeatherData wd=(WeatherData) o;
display(wd.getLow(),wd.getHeight(),wd.getWeather());
}
private void display(float low, float height, String weather) {
System.out.println("Iphone:最低温度-"+low+",最高温度-"+height+",天气"+weather+"。");
}
}
//Test类
package com.pattern.observer2;
public class Test {
public static void main(String[] args) {
WeatherData wd=new WeatherData();
Iphone i=new Iphone(wd);
wd.setData(12, 28, "sunny");
}
}
Java内置观察者的缺点
Observable是一个类,类只能继承一个父类,如果其有自己的父类,则无法继承观察者模式了关于观察者模式
- 该模式定义了对象之间一对多的关联
- 主题用一个共同的接口来更新观察者
- 主题和观察者之间用松耦合的方式结合,主题不知道观察者的细节,只知道观察者实现了观察者接口