学习笔记之观察者模式
首先要简单举个例子认识一下观察者模式
例:对于现实生活中出版者和订阅者之间的事情
1.报社的业务就是出版报纸
2.向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会已知收到新报纸。
3.当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来
4.只要报社还在运营,就会一直有人(或单位)向他们订阅包子或取消订阅
从这个例子中我们思考一下:报社一有新的报纸就会送给订报者,报社可以有多个订报者也就是一对多的依赖关系。当订报者取消订阅后报社就不再向他推送报纸。所以订报者可以动态的选择订阅或取消订阅。我们可以把报社称为一个主题,订报者称为观察者,报社有新的报纸时发布时所有的订报者便会收到最新的报纸,也就是主题一有改变所有的观察者都会更新状态。这个例子就相当于一个观察者模式。
通过这个例子我们总结出:出版者+订阅者=观察者模式
接下来展示几张图片大家会更加理解观察者模式:
看完这两个例子我们脑子中应该已经有一个对观察者模式比较形象的理解,下来来说一下观察者模式的定义
观察者模式:定义对象之间的一对多依赖,当一个对象改变状态时,他的所有依赖者都会收到同绘制并自动更新。
下面我们以气象监测应用的例子进行类图及代码深入理解观察者模式
对于这个例子我们思考该怎样或观察者模式的类图
从这个图中我们可以看出松耦合的威力
关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类是谁,做了些什么或其他任何细节
任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者
有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了此观察者接口的对象。
我们可以独立的复用主题或观察者。如果我们在其他地方需要使用主题或观察者,可以轻易的复用,因为二者并非紧密耦合。
改变主题或观察者其中一方,并不会影响另一方。因为二者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由的改变他们
这也体现了模式设计的原则:为了交互对象之间的松耦合设计而努力
这样我们就可以清楚的知道观
察者模式的优点,它提供了一种对象设计,让主题和观察者之间松耦合,他们依然可以交互,但是不太清楚彼此的细节
松耦合的设计之所以让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
package observer.allinterfcae;
public interface Subject {
public void notifyObserver();
public void registerObserver(Observer o);
public void removeObserver(Observer o);
}
package observer.allinterfcae;
public interface Observer {
public void update(float temp,float humidity);
}
package observer.allinterfcae;
public interface DisplayElement {
public void display();
}
package observer.allinterfcae;
import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList
observers;
private float temperature;
private float humidity;
//构造函数
public WeatherData(){
observers=new ArrayList
();
}
//public
@Override
//注册对象
public void registerObserver(Observer o) {
// 把参数Observer o注册注册进数组里面
observers.add(o);
}
//删除对象
@Override
public void removeObserver(Observer o) {
//获取索引
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
@Override
public void notifyObserver() {
//把状态告诉每一个观察者,因为观察者都实现了updata()方法
for (Observer observer : observers) {
observer.update(temperature, humidity);
}
}
//当有数据更新时通知观察者
public void measurementsChanged(){
notifyObserver();
}
//用来测试数据
public void setMeasurements(float temperature,float hunmidity){
this.temperature=temperature;
this.humidity=hunmidity;
measurementsChanged();
}
}
package observer.allinterfcae;
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
//构造器需要weatherData对象,作为注册用
public CurrentConditionDisplay(WeatherData weatherdata) {
this.weatherData=weatherData;
weatherdata.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current condition:"+temperature+"F degree and"+humidity+"% humdity");
}
@Override
public void update(float temperature, float humidity) {
this.temperature=temperature;
this.humidity=humidity;
display();
}
}
package observer.allinterfcae;
public class Test {
public static void main(String[] args) {
WeatherData weatherData=new WeatherData();
CurrentConditionDisplay currentDisplay=new CurrentConditionDisplay(weatherData);
System.out.println("WeatherStation:");
weatherData.setMeasurements(80, 65);
weatherData.setMeasurements(82, 70);
weatherData.setMeasurements(78, 90);
}
}
通过代码几乎已经掌握的观察者模式使用方式,但是我们可以看到上述代码是通过主题向观察者强迫式的推送所有数据,但观察者不一定愿意接受所有数据我们还可以通过“拉”的方式让观察者接收自己想要的数据,因为所有未来可能还会增多,而每个报告版不一定要用到所有数据
接下来通过使用java内置的观察者模式运行代码这次运用“拉”数据的方式
首先我们需要运用类图更好的了解java内置观察者模式运作过程
我们可以看到java内置的
观察者模式中主题是一个类,所以需要WeatherData继承Observble,确实这有点违反设计原则中面向接口编程而不面向过程编程的原则。所以如果需要你可以自己写主题,并且不是很麻烦就像前面的代码一样。
package wetherobserver;
public interface DisplayElement {
public void display();
}
package wetherobserver;
import java.util.Observable;
public class WeatherData extends Observable{
private float tempreature;
private float humidity;
//构造方法
public WeatherData() {}
public void measurementsChanged(){
//将changed的值设置为true
setChanged();
//把状态告诉每一位观察者
notifyObservers();
}
public void setMeasurements(float tempreature,float humidity){
this.tempreature=tempreature;
this.humidity=humidity;
measurementsChanged();
}
public float getTempreature(){
return tempreature;
}
public float getHumidity(){
return humidity;
}
}
package wetherobserver;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
Observable observable;
public CurrentConditionDisplay(Observable observable) {
this.observable=observable;
observable.addObserver(this);
}
@Override
public void update(Observable obs, Object arg) {
if(obs instanceof WeatherData){
WeatherData weatherData=(WeatherData) obs;
this.temperature=weatherData.getTempreature();
this.humidity=weatherData.getHumidity();
display();
}
}
@Override
public void display() {
System.out.println("Current condition:"+temperature+"F degree and"+humidity+"% humdity");
}
}
package wetherobserver;
public class Test {
public static void main(String[] args) {
WeatherData weatherData=new WeatherData();
CurrentConditionDisplay currentDisplay=new CurrentConditionDisplay(weatherData);
System.out.println("Current WeatherStation:");
weatherData.setMeasurements(80, 65);
weatherData.setMeasurements(82, 70);
weatherData.setMeasurements(78, 90);
}
}