观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
这个模式可以通过 报纸订阅服务的过程进行描述:
当订阅者向出版商订阅了某一个杂志之后,只要这个出版商每月出了新的杂志,就会自动把杂志快递给已经向它订阅的用户。
这里的出版商就相当于观察者模式中的“一”,称为主题(Subject),订阅者相当于观察者模式中的“多”,称为观察者(Observer)。
订阅者想要收到每月的新杂志,就必须向出版商进行订阅。出版商只要有新的杂志出版 ,就自动将新的杂志送给订阅者。
下面看一个例子:
我们从气象站,取得天气数据对象 ,我们要把天气数据对象中的天气数据,发送给每个天气布告板。
只要天气数据对象中天气数据一更新,就同步把天气数据更新到天气布告板。
进行抽象 ,这里的天气数据对象就相当于主题,天气布告板就相当于观察者。
类图:
主题接口 Subject接口:
1 /** 2 * 这是 主题的接口类 Subject接口 3 * @author wly 4 * 5 */ 6 public interface Subject { 7 /** 8 * 向主题对象 ,注册一个传入的观察者对象 9 * @param observer 10 */ 11 public void registerObserver(Observer observer); 12 /** 13 * 从主题对象已经注册的观察者集合中,移除传入的指定的观察者对象 14 * @param observer 15 */ 16 public void removerObserver(Observer observer); 17 /** 18 * 主题对象调用这个方法,发送消息,通知已经向它注册的观察者对象 19 */ 20 public void notifyObservers(); 21 }
观察者接口 Observer接口:
/** * 这是观察者的接口 Observer接口 * @author wly * */ public interface Observer { /** * 提供一个状态更新的接口 , 在主题对象的notifyObservers()方法中,被调用 * @param temp 温度 * @param humidity 湿度 * @param pressure 气压 */ public void update(float temp , float humidity , float pressure); }
具体的主题实现类 WeatherData类:
1 /** 2 * 这是具体的主题对象 WeatherData类 3 * @author wly 4 * 5 */ 6 public class WeatherData implements Subject { 7 //声明一个list集合 用于存放向主题对象 注册的 观察者对象 8 private List<Observer> observersList ; 9 //温度变量 10 private float temp; 11 //湿度变量 12 private float humidity; 13 //气压变量 14 private float pressure; 15 16 public WeatherData() 17 { 18 this.observersList = new ArrayList<Observer>(); 19 } 20 21 //注册观察者对象 22 @Override 23 public void registerObserver(Observer observer) { 24 25 observersList.add(observer); 26 } 27 //移除观察者对象 28 @Override 29 public void removerObserver(Observer observer) { 30 //放回observer对象在 observersList中的索引 ,如果存在返回索引值,不存在返回-1 31 int i = observersList.indexOf(observer); 32 if(i >= 0) 33 { 34 observersList.remove(i); 35 } 36 } 37 //通知已经注册了观察者对象 38 @Override 39 public void notifyObservers() { 40 for(int i = 0 ; i < observersList.size() ; i++) 41 { 42 Observer observer = observersList.get(i); 43 observer.update(temp, humidity, pressure); 44 } 45 } 46 //模拟测量值变化 ,变化之后通知已经注册了观察者对象 47 public void setMeasure(float temp,float humidity,float pressure) 48 { 49 this.temp = temp; 50 this.humidity = humidity; 51 this.pressure = pressure; 52 53 notifyObservers(); 54 } 55 }
具体的观察者实现类 布告板 CurrentConditionDisplay类:
1 /** 2 * 这是观察者对象 天气情况布告板 CurrentConditionDisplay类 3 * @author wly 4 * 5 */ 6 public class CurrentConditionDisplay implements Observer { 7 private float temp; //温度 8 private float humidity; //湿度 9 private float pressure; //气压 10 11 @Override 12 public void update(float temp, float humidity, float pressure) { 13 this.temp = temp; 14 this.humidity = humidity; 15 this.pressure = pressure; 16 } 17 18 //布告板的显示信息 19 public void display() 20 { 21 System.out.println("当前天气情况如下:"); 22 System.out.println("温度:"+temp); 23 System.out.println("湿度:"+humidity); 24 System.out.println("气压:"+pressure); 25 } 26 }
最后提供一个测试类:
1 public class TestClass { 2 3 public static void main(String[] args) { 4 //创建两个观察者对象 布告板1 和 布告板2 5 CurrentConditionDisplay ccd1 = new CurrentConditionDisplay(); 6 CurrentConditionDisplay ccd2 = new CurrentConditionDisplay(); 7 8 //创建一个主题对象 天气数据对象 9 WeatherData wd = new WeatherData(); 10 //将两个观察者对象布告板 向主题对象进行注册 11 wd.registerObserver(ccd1); 12 wd.registerObserver(ccd2); 13 //模拟 天气数据变化 14 wd.setMeasure(25.2F, 36.6F, 82.9F); 15 16 //显示布告板的天气信息 17 ccd1.display(); 18 ccd2.display(); 19 20 } 21 }
运行结果如下:
在JDK中内置类 一个Observerable类 和 Observer接口 。
其中这里的Observerable类相当于我们所写的 Subject接口。 Observerable类已经提供了 注册,移除,通知等方法。
我们在定义一个具体主题类时,直接继承Observerable类就可以继承那些方法,不需要自己写。
Observer接口就相当于我们所写的 Observer接口,这个接口中只有一个update方法,供具体的观察者类去实现。