观察者模式
观察者模式:定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
原则:封装变化;多用组合,少用继承;针对接口编程,不针对实现编程;为交互对象之间的松耦合设计而努力。
Subject是被观察者接口,WeatherData是真正的被观察者实现类,Observer是观察者接口,下面的三个是具体的观察者。每个观察者进来的时候要实现Observer接口,然后还要向Subject中注册自己,以告诉被观察者,当发生变化时通知自己。当发生变化时,Subject调用notifyObservers()方法,在这个方法中调用update()方法来通知观察者更新。具体的代码如下:
Subject接口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface Subject { 2 public void registerObserver(Observer o); 3 public void removeObserver(Observer o); 4 public void notifyObservers(); 5 }
WeatherData实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class WeatherData implements Subject { 2 private ArrayList observers; 3 private float temperature; 4 private float humidity; 5 private float pressure; 6 7 public WeatherData() { 8 observers = new ArrayList(); 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 40 public float getTemperature() { 41 return temperature; 42 } 43 44 public float getHumidity() { 45 return humidity; 46 } 47 48 public float getPressure() { 49 return pressure; 50 } 51 }
Observer接口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface Observer { 2 public void update(float temp, float humidity, float pressure); 3 }
DisplyElement接口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public interface DisplayElement { 2 public void display(); 3 }
CurrentConditionsDisply观察者实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class CurrentConditionsDisplay implements Observer, DisplayElement { 2 private float temperature; 3 private float humidity; 4 private Subject weatherData; 5 6 public CurrentConditionsDisplay(Subject weatherData) { 7 this.weatherData = weatherData; 8 weatherData.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 19 + "F degrees and " + humidity + "% humidity"); 20 } 21 }
StatisticsDisply观察者实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class StatisticsDisplay implements Observer, DisplayElement { 2 private float maxTemp = 0.0f; 3 private float minTemp = 200; 4 private float tempSum= 0.0f; 5 private int numReadings; 6 private WeatherData weatherData; 7 8 public StatisticsDisplay(WeatherData weatherData) { 9 this.weatherData = weatherData; 10 weatherData.registerObserver(this); 11 } 12 13 public void update(float temp, float humidity, float pressure) { 14 tempSum += temp; 15 numReadings++; 16 17 if (temp > maxTemp) { 18 maxTemp = temp; 19 } 20 21 if (temp < minTemp) { 22 minTemp = temp; 23 } 24 25 display(); 26 } 27 28 public void display() { 29 System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) 30 + "/" + maxTemp + "/" + minTemp); 31 } 32 }
ForeastDisply观察者实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class ForecastDisplay implements Observer, DisplayElement { 2 private float currentPressure = 29.92f; 3 private float lastPressure; 4 private WeatherData weatherData; 5 6 public ForecastDisplay(WeatherData weatherData) { 7 this.weatherData = weatherData; 8 weatherData.registerObserver(this); 9 } 10 11 public void update(float temp, float humidity, float pressure) { 12 lastPressure = currentPressure; 13 currentPressure = pressure; 14 15 display(); 16 } 17 18 public void display() { 19 System.out.print("Forecast: "); 20 if (currentPressure > lastPressure) { 21 System.out.println("Improving weather on the way!"); 22 } else if (currentPressure == lastPressure) { 23 System.out.println("More of the same"); 24 } else if (currentPressure < lastPressure) { 25 System.out.println("Watch out for cooler, rainy weather"); 26 } 27 } 28 }
测试:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class WeatherStation { 2 3 public static void main(String[] args) { 4 WeatherData weatherData = new WeatherData(); 5 6 CurrentConditionsDisplay currentDisplay = 7 new CurrentConditionsDisplay(weatherData); 8 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); 9 ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); 10 11 weatherData.setMeasurements(80, 65, 30.4f); 12 weatherData.setMeasurements(82, 70, 29.2f); 13 weatherData.setMeasurements(78, 90, 29.2f); 14 } 15 }
小结:
- 这个模式是松偶合的。改变主题或观察者中的一方,另一方不会受到影像。
- JDK中也有自带的观察者模式。但是被观察者是一个类而不是接口,限制了它的复用能力。
- 在JavaBean和Swing中也可以看到观察者模式的影子。
观察者模式的优点
(1)具体主题和具体观察者是松耦合关系。由于主题接口仅仅依赖于观察者接口,因此具体主题只是知道它的观察者是实现观察者接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题接口,因此具体观察者只是知道它依赖的主题是实现主题接口的某个类的实例,但不需要知道具体是哪个类。
(2)观察者模式满足“开-闭原则”。主题接口仅仅依赖于观察者接口,这样,就可以让创建具体主题的类也仅仅是依赖于观察者接口,因此,如果增加新的实现观察者接口的类,不必修改创建具体主题的类的代码。。同样,创建具体观察者的类仅仅依赖于主题接口,如果增加新的实现主题接口的类,也不必修改创建具体观察者类的代码。
适合使用观察者模式的情景
(1)当一个对象的数据更新时需要通知其他对象,但这个对象又不希望和被通知的那些对象形成紧耦合。
(2)当一个对象的数据更新时,这个对象需要让其他对象也各自更新自己的数据,但这个对象不知道具体有多少对象需要更新数据。