当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。
一:需求
当weatherData获取到气象站穿过来的数据的时候展示在显示装置上。 二,错误的范例
伪代码:
1 public void measurementsChanged( ) { 2 float temp = getTemperature( ); 3 float humidity = getHumidity( ); 4 float pressure = getPressure( ); 5 currentConditionsDisplay.update(temp, humidity, pressure); 6 statisticsDisplay.update(temp, humidity, pressure); 7 forecastDisplay.update(temp, humidity, pressure); 8 }
面的代码有什么问题吗?
三,认识观察者设计模式
3.1,出版者+订阅者=观察者模式
如果你了解报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样:出版者改称为"主题"(Subject),订阅者改称为"观察者"(Observer)。
3.2,定义观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式定义了一系列对象之间的一对多关系。当一个对象改变状态,其他依赖者都会收到通知。
主题和观察者定义了一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格,观察者可能因此新值而更新。
3.3,设计原则(第三个设计原则)为了交互对象之间的松耦合设计而努力当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
为什么呢?
关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何时候删除某些观察者。
有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里实现此观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
我们可以独立地复用主题或观察者。如果我们在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合。
改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。
代码实现
Subject.java(主题)
1 public interface Subject { 2 // 注册 3 public void registerObserver(Observer o); 4 5 // 注销 6 public void removeObserver(Observer o); 7 8 // 通知 9 public void notifyObservers(); 10 }
eatherData.java(主题的实现类)
1 public class WeatherData implements Subject { 2 private ArrayList<Observer> observers; 3 private float temperature; 4 private float humidity; 5 private float pressure; 6 7 public WeatherData() { 8 observers = new ArrayList<Observer>(); 9 } 10 11 public void registerObserver(Observer o) { 12 observers.add(o); 13 } 14 15 public void removeObserver(Observer o) { 16 int i = observers.indexOf(o); 17 if (i >= 0) { 18 observers.remove(i); 19 } 20 } 21 22 public void notifyObservers() { 23 for (int i = 0; i < observers.size(); i++) { 24 Observer observer = (Observer) observers.get(i); 25 observer.update(temperature, humidity, pressure); 26 } 27 } 28 29 public void measurementsChanged() { 30 notifyObservers(); 31 } 32 33 public void setMeasurements(float temperature, float humidity, float pressure) { 34 this.temperature = temperature; 35 this.humidity = humidity; 36 this.pressure = pressure; 37 measurementsChanged(); 38 } 39 }
bserver.java(观察者)
1 //观察者 2 3 public interface Observer { 4 5 public void update(float temp, float humidity, float pressure);
isplayElement.java(展示板)
1 public interface DisplayElement { 2 public void display();
urrentConditionsDisplay.java(观察者1)
1 public class CurrentConditionsDisplay implements Observer, DisplayElement { 2 private float temperature; 3 private float humidity; 4 private Subject subject; 5 6 public CurrentConditionsDisplay(Subject weatherData) { 7 this.subject = weatherData; 8 subject.registerObserver(this); 9 } 10 11 public void update(float temperature, float humidity, float pressure) { 12 this.temperature = temperature; 13 this.humidity = humidity; 14 display(); 15 } 16 17 public void display() { 18 System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); 19 } 20 }
ForecastDisplay.java(观察者2)
1 public class ForecastDisplay implements DisplayElement, Observer { 2 private float temperature; 3 private float humidity; 4 private Subject subject; 5 6 public ForecastDisplay(Subject weatherData) { 7 this.subject = weatherData; 8 weatherData.registerObserver(this); 9 } 10 11 @Override 12 public void update(float temp, float humidity, float pressure) { 13 this.temperature = temperature; 14 this.humidity = humidity; 15 display(); 16 } 17 18 @Override 19 public void display() { 20 System.out.println("Forecast conditions: " + temperature + "F degrees and " + humidity + "% humidity"); 21 } 22 }
StatisticsDisplay.java(观察者3)
1 public class StatisticsDisplay implements DisplayElement, Observer { 2 3 private float temperature; 4 5 private float humidity; 6 7 private Subject weatherData; 8 9 10 11 public StatisticsDisplay(Subject weatherData) { 12 13 this.weatherData = weatherData; 14 15 weatherData.registerObserver(this); 16 17 } 18 19 20 21 @Override 22 23 public void update(float temp, float humidity, float pressure) { 24 25 this.temperature = temperature; 26 27 this.humidity = humidity; 28 29 display(); 30 31 } 32 33 34 35 @Override 36 37 public void display() { 38 39 System.out.println("StatisticsDisplay conditions: " + temperature 40 41 + "F degrees and " + humidity + "% humidity"); 42 43 } 44 45 }
Test.java(测试)
1 /** 2 * @author 88403097 3 * @date 2018年9月20日 说明: 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新 4 *主题变化,weatherData变化通知CurrentConditionsDisplay,StatisticsDisplay,ForecastDisplay发生变化 5 *下面来分析一下是怎么产生的,看一下原理 6 *主题和观察者之间是若耦合。 7 *一,把架子搭好。 8 * 1.1,主题的接口写好,三个抽象方法(注册,取消,通知)Subject 9 * 1.2,观察者的接口写好,(更新)Observer 10 *二,实现 11 * 2.1,实现主题的方法定义一个list假如注册就往list添加对象,假如注销就往list删除对象 12 * ,假如通知,就从list中便利对象然后调用update(更新)的方法, 13 * 2.2,观察者的实现注意上面调用update(更新)的方法,这个时候已经确定运行时候确定调用者了,那么 14 * 这个时候我们直接实现更新的方法就行了。 15 * 2.3,至于这个展示,就好说了,只是在update的时候调用了一下display的方法,因为要统一的设计所以也设计了一个接口 16 *三,补充 17 * 3.1,注册的逻辑,在观察者的实现中我们还能看到,里面关联着主题的接口,方便我们注册和注销用,在这里为了方便起见 18 * 初始化的时候就调用了注册的方法,所以只要调用构造方法就注册成功了。 19 * 3.2,所谓的增加和修改程序都是对已经有了的类,接口或者方法来说的。比如说这里假如增加一个Display这个时候不需要修改原来的代码 20 * 逻辑的,只需要增加一个Display实现类继承Observer就可以了 21 * 22 * 一:注册,把观察者注入到主题中来,规定谁有权限接受到主题的通知 23 1,new对象的时候,把主题的实现类作为参数传入到观察者的实现类中来 24 2,这样的话在观察者的构造方法中就有了主题的实现类,在观察者的构造方法中在调用主题接口的方法,因为这个时候已经确定谁是主题的实现类了(传过来的就是,主要是动态主题的) 25 这样就会到主题的实现类中来调用注册的方法了,无非就是一个list中的add方法。 26 3,这样在主题的list的方法中就有了观察者了,因为这个时候主题的实例还没有销毁,所以还在内存中的。 27 二:主题修改通知观察者 28 现在观察者已经注册完毕了,现在我们调用主题的实现类里面的参数修改的方法,修改会调用统一定义的方法(update),因为观察者统一实现了这个方法,并且观察者全部在注册的list中,这样一来 29 就可以直接调用观察者的统一定义的方法来实现通知观察者的目的了。 30 */ 31 public class Test { 32 @SuppressWarnings("unused") 33 public static void main(String[] args) { 34 WeatherData weatherData = new WeatherData(); 35 // 这里是依赖者 36 // 这一行相当于注册,通过构造方法 37 CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); 38 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); 39 ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); 40 /* 41 * 建立三个布告板,并把WeatherData对象传给它们。 42 */ 43 // 主题变化,weatherData变化通知CurrentConditionsDisplay,StatisticsDisplay,ForecastDisplay发生变化 44 weatherData.setMeasurements(80, 65, 30.4f); 45 weatherData.setMeasurements(82, 70, 29.2f); 46 weatherData.setMeasurements(78, 90, 29.2f); 47 48 } 49 } 50 /* 51 * 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。 观察者模式提供了一种对象设计,让主题和观察者之间松耦合。 52 * 为什么呢? 关于观察者的一切,主题只知道观察者实现了某个接口(也就是Observer接口)。主 53 * 题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。 任何时候我们都可以增加新的观察者。 54 * 因为主题唯一依赖的东西是一个实现 Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可 55 * 以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的,也可以在任何 时候删除某些观察者。 56 * 有新类型的观察者出现时,主题的代码不需要修改。假如我们有个新的具体类需要当 57 * 观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新的类里 实现此观察者接口,然后注册为观察者即可。 58 * 主题不在乎别的,它只会发送通知给所 有实现了观察者接口的对象。 59 * 我们可以独立地复用主题或观察者。如果我们在其他地方需要使用主题或观察者,可 以轻易地复用,因为二者并非紧耦合。 60 * 改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他 61 * 们之间的接口仍被遵守,我们就可以自由地改变他们。 设计原则 为了交互对象之间的松耦合设计而 努力。 62 * 松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化, 是因为对象之间的互相依赖降到了最低。 63 */
55