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是一个类,类只能继承一个父类,如果其有自己的父类,则无法继承观察者模式了关于观察者模式
- 该模式定义了对象之间一对多的关联
- 主题用一个共同的接口来更新观察者
- 主题和观察者之间用松耦合的方式结合,主题不知道观察者的细节,只知道观察者实现了观察者接口