定义

观察者模式定义了对象之间的一对多依赖, 当一个对象改变状态时, 它的所有依赖者都会收到通知并自动更新
**

设计原则

  • 为交互对象之间的松耦合设计而努力

案例

背景: 一个气象观测站, 由weatherObject追踪天气, 温度, 气压, 有三种布告板分别显示目前的状况, 并希望布告板可以增加
image.png

建立Subject接口, 负责Observer的注册,移除以及通知

  1. public interface Subject {
  2. void registerObserver(Observer observer);
  3. void removeObserver(Observer observer);
  4. void notifyObserver(String newTemp);
  5. }

WeatherData实现Subject接口, 持有List, 实现具体的注册(add), 移除(remove)以及通知(通知所有的观察者)

  1. public class WeatherData implements Subject {
  2. private List<Observer> observerList;
  3. private String temperature;
  4. public WeatherData() {
  5. observerList = new ArrayList<Observer>();
  6. }
  7. public void change(String newTemp) {
  8. System.out.println("气候改变了!");
  9. notifyObserver(newTemp);
  10. }
  11. public String getTemperature() {
  12. return temperature;
  13. }
  14. public void setTemperature(String temperature) {
  15. this.temperature = temperature;
  16. }
  17. @Override
  18. public void registerObserver(Observer observer) {
  19. observerList.add(observer);
  20. }
  21. @Override
  22. public void removeObserver(Observer observer) {
  23. int i = observerList.indexOf(observer);
  24. if (i > 0) {
  25. observerList.remove(i);
  26. }
  27. }
  28. @Override
  29. public void notifyObserver(String newTemp) {
  30. for (Observer observer : observerList) {
  31. observer.update(newTemp);
  32. }
  33. }
  34. }

Observer接口有更新方法

  1. public interface Observer {
  2. void update(String newTemp);
  3. }

布告板实现Observer接口, 实现更新方法, 更新自己的变量
并持有weather对象, 在构造方法中将自身注册到Subject的List

  1. public class OneDisplay implements Observer, Display {
  2. private String temp;
  3. private Subject weather;
  4. public OneDisplay(Subject weather) {
  5. this.weather = weather;
  6. weather.registerObserver(this);
  7. }
  8. @Override
  9. public void display() {
  10. System.out.println("这里是一号公告板, 目前的温度是: " + this.temp);
  11. }
  12. public String getTemp() {
  13. return temp;
  14. }
  15. public void setTemp(String temp) {
  16. this.temp = temp;
  17. }
  18. @Override
  19. public void update(String newTemp) {
  20. this.temp = newTemp;
  21. }
  22. }

测试:

  1. @Test
  2. public void testObserver() {
  3. WeatherData weatherData = new WeatherData();
  4. OneDisplay oneDisplay = new OneDisplay(weatherData);
  5. TwoDisplay twoDisplay = new TwoDisplay(weatherData);
  6. weatherData.change("888");
  7. oneDisplay.display();
  8. twoDisplay.display();
  9. weatherData.change("999");
  10. oneDisplay.display();
  11. twoDisplay.display();
  12. }
  13. 结果:
  14. 气候改变了!
  15. 这里是一号公告板, 目前的温度是: 888
  16. 这里是二号公告板, 目前的温度是: 888
  17. 气候改变了!
  18. 这里是一号公告板, 目前的温度是: 999
  19. 这里是二号公告板, 目前的温度是: 999

这样做的好处:
1.Subject与Observer完全松耦合, Observer可以自由增加, 将自身注册到Subject中即可
2.面对接口编程
3.当Subject发生变化时, 通知所有的Observer, 所有的Observer会执行update
联系以上, 即容易理解概念


java内置的观察者模式
被观察者继承Observable类, 观察者实现Observer接口实现
相关方法有 setChange, clearChange, notifyObservers等

问题:
Observable类限制了复用, 继承的方法拓展性不好
java不支持多继承, 不是接口无法添加自己的实现和原有的API搭配使用, setChanged()等方法的保护级别为protected, 外部无法覆盖, 只能通过继承覆盖

image.png
**
被观测者继承 java.util.Observable 类
通知需要先setchange(), 再notifyObservers(arg);

  1. public class WeatherData extends Observable {
  2. private String temp;
  3. public void tempChange(String newTemp) {
  4. this.setTemp(newTemp);
  5. setChanged();
  6. notifyObservers("测试传参");
  7. }
  8. public String getTemp() {
  9. return temp;
  10. }
  11. public void setTemp(String temp) {
  12. this.temp = temp;
  13. }
  14. }

观察者实现java.util.Observer接口, 持有Observable属性
并在构造方法中将自身注册, 重写update方法接收消息

  1. public class Display1 implements Observer, Display {
  2. Observable observable;
  3. private String disTemp;
  4. public Display1(Observable observable) {
  5. this.observable = observable;
  6. observable.addObserver(this);
  7. }
  8. @Override
  9. public void update(Observable o, Object arg) {
  10. if (o instanceof WeatherData) {
  11. WeatherData data = (WeatherData) o;
  12. this.disTemp = data.getTemp();
  13. System.out.println("接收到的参数为: " + (String) arg);
  14. }
  15. }
  16. public String getDisTemp() {
  17. return disTemp;
  18. }
  19. public void setDisTemp(String disTemp) {
  20. this.disTemp = disTemp;
  21. }
  22. @Override
  23. public void display() {
  24. System.out.println("这里是布告板1,现在的温度是: " + this.disTemp);
  25. }
  26. }

**
测试

  1. @Test
  2. public void testStrategy() {
  3. WeatherData weatherData = new WeatherData();
  4. weatherData.setTemp("12度");
  5. Display1 display1 = new Display1(weatherData);
  6. Display2 display2 = new Display2(weatherData);
  7. display1.display();
  8. display2.display();
  9. weatherData.tempChange("20度");
  10. display1.display();
  11. display2.display();
  12. }
  13. 结果:
  14. 这里是布告板1,现在的温度是: null
  15. 这里是布告板2,现在的温度是: null
  16. 接收到的参数为: 测试传参
  17. 接收到的参数为: 测试传参
  18. 这里是布告板1,现在的温度是: 20
  19. 这里是布告板2,现在的温度是: 20