设计模式--观察者模式(五)
2017-06-09 22:53
169 查看
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
知识点的梳理:
为了交互对象之间的松耦合设计而努力;
主题(也就是可观察者)用一个共同的接口来更新观察者;
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口;
有多个观察者时,不可以依赖特定的通知次序;
Swing大量使用观察者模式,许多GUI框架也是如此;
此模式也被应用到许多地方,如JavaBean,RMI;
气象监测应用的概况
此系统中的三个部分是气象站(获取实际气象数据的物理装置),WeatherData对象(追踪自来气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看);
WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度,湿度,气压),气象统计和天气预报;
我们需要建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况,气象统计和天气预报;
我们现在知道什么?
WeatherData类具有getter方法,可以取得三个测量值:温度,湿度与气压:
当新的测量数据备妥时,measurementsChanged()方法就会被调用:
需要实现三个使用天气数据的布告板:"目前状况"布告,"气象统计"布告,"天气预报"布告。一旦WeatherData有新的测量,这些布告必须马上更新;
此系统可扩展,让其他开发人员建立定制的布告板,用户可以随心所欲地添加或删除任何布告板。
错误示范
在measurementsChanged()方法中添加代码:
实现有什么不对?
什么是观察者模式
报纸和杂志的订阅是怎么回事?
报社的业务就是出版报纸;
向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸;
当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸;
只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸;
出版者+订阅者=观察者模式
结合报纸的订阅:出版者改称为"主题"(subject),订阅者改称为"观察者"(Observer);
图解观察者模式
定义观察者模式:类图
松耦合的威力
两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节;
对于观察者,主题只知道观察者实现了某个接口(也就是Observer接口),主题不需要知道观察者的具体类是谁,做了些什么或其他任何细节;
任何时候都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以可以随时增加观察者。实际上,在运行的时候,可以用新的观察者取代现有的观察者,主题不会受到任何影响。同理,也可以在任何时候删除这些观察者;
有新类型的观察者出现时,主题的代码不需要修改。
加入我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可;
主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象;
可以独立地复用主题或者观察者。如果我们在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合;
改变主题或者观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,就可以自由的改变它们;
设计气象站
WeatherData类符合观察者模式中,所说的"一",而"多"正是使用天气观测的各种布告板;
如何将气象观测值放到布告板上呢?
如果将WeatherData对象当作主题,把布告板当作观察者,布告板为了取得信息,就必须先向WeatherData对象注册;
一旦WeatherData知道有某个布告板的存在,就会适时地调用布告板的某个方法来告诉布告板观测值是多少;
每个布告板都有差异,需要一个共同的接口。虽然布告板的类不一样,但它们应该实现相同的jie'k
所以,每个布告板都应该有一个名为update()的方法,以供WeatherData对象调用;而这个方法应该在所有布告板都实现的共同接口里定义;
设计图
实现气象站
先建立一些接口
在WeatherData中实现主题接口
建立布告板
需要三个布告板:目前状况布告板,统计布告板,预测布告板。先来实现目前状况布告板
启动气象站
先建立一个测试程序
效果:
使用Java内置的观察者模式
java.util包(package)内包含最基本的Observer接口与Observable类,这和Subject接口与Observer接口很相似;
有了Java内置的支持,只需要扩展(继承)Observable,并告诉它何时该通知观察者就可以了;
java.util.Observer和java.util.Observable的类结构
如何把对象变成观察者?
实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者,调用deleteObserver()方法即可;
观察者如何送出通知?
需要利用扩展java.util.Observable接口产生"可观察者"类,然后,需要两个步骤:
先调用setChanged()方法,标记状态已经改变的事实;
然后调用两种notifyObservers()方法中的一个:
notifyObservers();
notifyObservers(Object arg);//当通知时,此版本可以传送任何的数据对象给每一个观察者
观察者如何接受通知?
同以前一样,观察者实现更新方法,但签名不一样:
推送数据给观察者,可以把数据当作数据对象传送给ntifyObservers(args)方法。否则,观察者就必须从可观察者对象中"拉"(pull)数据。
利用内置的支持重做气象站
首先,把WeatherData改成使用java.util.Observable
现在重做CurrentConditionsDisplay
继续使用WeatherStation进行测试,效果如下:其实是一样的!
java.util.Observable的缺点
它一个类,而非接口;
Observable的setChanged()方法的访问修饰符为protected。除非我们继承自Observable,否则无法创建Observable实例,并组合到自己的对象中。这违反了第二个设计原则:"多用组合,少用继承";
知识点的梳理:
为了交互对象之间的松耦合设计而努力;
主题(也就是可观察者)用一个共同的接口来更新观察者;
观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口;
有多个观察者时,不可以依赖特定的通知次序;
Swing大量使用观察者模式,许多GUI框架也是如此;
此模式也被应用到许多地方,如JavaBean,RMI;
气象监测应用的概况
此系统中的三个部分是气象站(获取实际气象数据的物理装置),WeatherData对象(追踪自来气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看);
WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度,湿度,气压),气象统计和天气预报;
我们需要建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况,气象统计和天气预报;
我们现在知道什么?
WeatherData类具有getter方法,可以取得三个测量值:温度,湿度与气压:
getTemperature(); getHumidity(); getPressure(); |
measurementsChanged() |
此系统可扩展,让其他开发人员建立定制的布告板,用户可以随心所欲地添加或删除任何布告板。
错误示范
在measurementsChanged()方法中添加代码:
public class WeatherData { public void measurementsChanged(){ //调用WeatherData的三个getXxx()方法,以取得最近的测量值。这些getXxx()方法已经实现好了 float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); //更新布告板 currentConditionsDisplay.update(temp,humidity,pressure); statisticsDisplay.update(temp,humidity,pressure); forecastDisplay.update(temp,humidity,pressure); } //其他WeatherData方法 } |
什么是观察者模式
报纸和杂志的订阅是怎么回事?
报社的业务就是出版报纸;
向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸;
当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸;
只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸;
出版者+订阅者=观察者模式
结合报纸的订阅:出版者改称为"主题"(subject),订阅者改称为"观察者"(Observer);
图解观察者模式
定义观察者模式:类图
松耦合的威力
两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节;
对于观察者,主题只知道观察者实现了某个接口(也就是Observer接口),主题不需要知道观察者的具体类是谁,做了些什么或其他任何细节;
任何时候都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以可以随时增加观察者。实际上,在运行的时候,可以用新的观察者取代现有的观察者,主题不会受到任何影响。同理,也可以在任何时候删除这些观察者;
有新类型的观察者出现时,主题的代码不需要修改。
加入我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可;
主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象;
可以独立地复用主题或者观察者。如果我们在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合;
改变主题或者观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,就可以自由的改变它们;
设计气象站
WeatherData类符合观察者模式中,所说的"一",而"多"正是使用天气观测的各种布告板;
如何将气象观测值放到布告板上呢?
如果将WeatherData对象当作主题,把布告板当作观察者,布告板为了取得信息,就必须先向WeatherData对象注册;
一旦WeatherData知道有某个布告板的存在,就会适时地调用布告板的某个方法来告诉布告板观测值是多少;
每个布告板都有差异,需要一个共同的接口。虽然布告板的类不一样,但它们应该实现相同的jie'k
所以,每个布告板都应该有一个名为update()的方法,以供WeatherData对象调用;而这个方法应该在所有布告板都实现的共同接口里定义;
设计图
实现气象站
先建立一些接口
public interface Subject { //这两个方法都需要一个观察者作为变量,该观察者是用来注册或被删除的 void registerObserver(Observer o); void removeObserver(Observer o); //当主题状态改变时,这个方法会被调用,以通知所有的观察者 void notifyObservers(); } |
public interface Observer { /** * 所有观察者都必须实现此方法,以实现观察者接口。把观测值传入观察者 * 当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者 */ void update(float temp,float humidity,float pressure); } |
/* * 该接口只包含一个方法。当布告板需要显示时,调用此方法。 */ public interface DisplayElement { public void display(); } |
public class WeatherData implements Subject { //此字段用来记录观察者,在构造函数中建立 private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData(){ observers = new ArrayList(); } //当从气象站得到更新观测值时,我们通知观察者< 16d15 span style="font-family:'宋体';"> public void measurementsChanged(){ notifyObservers(); } //当注册观察者时,只要把它加到ArrayList的后面即可 @Override public void registerObserver(Observer o) { observers.add(o); } //当观察者想取消注册,我们把它从ArrayList中删除即可 @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if(i >= 0 ){ observers.remove(i); } } //把状态告诉每一个观察者。因为观察者都实现了update(),所以可以通知它们 @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer) observers.get(i); observer.update(temperature, humidity, pressure); } } //利用这个方法来测试布告板 public void setMeasurements(float temperature,float humididty,float pressure){ this.temperature = temperature; this.humidity = humididty; this.pressure = pressure; measurementsChanged(); } //其他WeatherData方法 } |
需要三个布告板:目前状况布告板,统计布告板,预测布告板。先来实现目前状况布告板
/** * 实现了Observer接口,所以可以从WeatherData对象中获得改变 * 实现了DisplayElement接口,因为我们的API规定所有的布告板都必须实现此接口 */ public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private Subject weatherData; //构造器需要weatherData对象(也就是主题)作为注册使用 public CurrentConditionsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } //该方法只是显示最近的温度和湿度 @Override public void display() { System.out.println("Current conditions:"+temperature+"F degrees and"+humidity+"% humidity"); } @Override public void update(float temp, float humidity, float pressure) { //当update()被调用时,我们把温度和湿度保存起来,然后调用display() this.temperature =temperature; this.humidity = humidity; display(); } } |
先建立一个测试程序
public class WeatherStation { public static void main(String[] args) { //首先,建立一个WeatherData对象 WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); weatherData.setMeasurements(80, 65, 30.4f); weatherData.setMeasurements(82, 70, 29.2f); weatherData.setMeasurements(78, 90, 29.2f); } } |
java.util包(package)内包含最基本的Observer接口与Observable类,这和Subject接口与Observer接口很相似;
有了Java内置的支持,只需要扩展(继承)Observable,并告诉它何时该通知观察者就可以了;
java.util.Observer和java.util.Observable的类结构
如何把对象变成观察者?
实现观察者接口(java.util.Observer),然后调用任何Observable对象的addObserver()方法。不想再当观察者,调用deleteObserver()方法即可;
观察者如何送出通知?
需要利用扩展java.util.Observable接口产生"可观察者"类,然后,需要两个步骤:
先调用setChanged()方法,标记状态已经改变的事实;
然后调用两种notifyObservers()方法中的一个:
notifyObservers();
notifyObservers(Object arg);//当通知时,此版本可以传送任何的数据对象给每一个观察者
观察者如何接受通知?
同以前一样,观察者实现更新方法,但签名不一样:
推送数据给观察者,可以把数据当作数据对象传送给ntifyObservers(args)方法。否则,观察者就必须从可观察者对象中"拉"(pull)数据。
利用内置的支持重做气象站
首先,把WeatherData改成使用java.util.Observable
package hey.up2; //导入Java内置观察者package import java.util.Observable; import java.util.Observer; //现在继承Observable public class WeatherData extends Observable { private float temperature; private float humidity; private float pressure; public WeatherData(){ //不再需要构造函数来记住观察者们了 } //当从气象站得到更新观测值时,我们通知观察者 public void measurementsChanged(){ //在调用notifyObservers函数之前,要先调用serChanged()来指示状态已经改变 setChanged(); notifyObservers(); } //利用这个方法来测试布告板 public void setMeasurements(float temperature,float humididty,float pressure){ this.temperature = temperature; this.humidity = humididty; this.pressure = pressure; measurementsChanged(); } //观察者需要这些getter方法,取得WeatherData对象的状态 public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } } |
package hey.up2; import java.util.Observable; import java.util.Observer; /** * 实现java.util.Observer接口 */ public class CurrentConditionsDisplay implements Observer, DisplayElement { Observable observable; private float temperature; private float humidity; //现在构造函数需要一个Observable当参数,并将CurrentCondi-tionsDisplay对象登记成为观察者 public CurrentConditionsDisplay(Observable observable){ this.observable = observable; observable.addObserver(this); } //改变update方法,增加Observable和数据对象作为参数 @Override public void update(Observable obs, Object arg) { //此方法先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和湿度测试值,最后调用display() if(obs instanceof WeatherData){ WeatherData weatherData = (WeatherData)obs; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } //该方法只是显示最近的温度和湿度 @Override public void display() { System.out.println("Current conditions:"+temperature+"F degrees and"+humidity+"% humidity"); } } |
它一个类,而非接口;
Observable的setChanged()方法的访问修饰符为protected。除非我们继承自Observable,否则无法创建Observable实例,并组合到自己的对象中。这违反了第二个设计原则:"多用组合,少用继承";
相关文章推荐
- 设计模式与泡mm的关系之Observer观察者模式及再思考
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
- 设计模式随笔系列:气象站的故事-观察者模式(Observer)[原]
- 利用观察者模式设计仿真器内核接口
- .NET中的设计模式五:观察者模式
- 设计模式之Observer观察者模式
- 认识观察者模式(设计模式)[转载]
- 设计模式学习笔记(二十一)——Observer观察者
- 用 spring 实现观察者设计模式
- .NET中的设计模式五:观察者模式 选择自 lane_cn 的 Blog
- 设计模式之观察者模式 (原创)
- 设计模式之观察者模式(Observer Pattern)(一)
- 设计模式之观察者模式
- IssueVision 学习笔记(三)-----设计模式之OBSERVER(观察者)模式
- 设计模式之观察者模式(Observer Pattern)(二)
- 探究观察者设计模式
- GEF框架中的设计模型(观察者模式)
- C#设计模式-观察者observer模式实现
- AspectJ实现设计模式(一)——观察者模式