Java设计模式之观察者模式
摘要:观察者模式(ObserverPattern)。用于实时监测某些Object的动态、只要Object一改变、那么他的所有观察者Observer都会知道、之后观察者会根据Object的改变进行下一步操作。这个在SWING编程中是最常见的。那些Listener就是观察者。
一:问题的引出
要实现一个天气预报的功能、当天气的数据发生变化的时候、会实时的以三种形式来显示天气:当前天气情况、统计分析情况、天气预报。有可能还会有第四种显示。
这里我们不管数据是如何来的、就假设我们已经能获取到数据了。在程序中是通过调用被观察的对象的notifyObserver方法来通知所有Observer来实现的。
二:问题分析
1、通过分析我们得出一个简单的总结:就是天气数据一旦有更新、那么就要实时的改变显示。观察者模式可以很好的解决这种模型的问题。
2、既然确定要使用观察者模型、就要定位出谁是观察者、谁是被观察者、这里有个简单的原则、观察者与被观察者之间的关系是多对一的关系、也就是说被观察者只有一个、那么就是我们的天气数据信息、观察者则是三种要显示不同信息的终端(当然也可以有第四个观察者)。
3、因为每个观察者都要动态的显示信息、所以我们应该抽象出来一个显示信息的类或者接口。
4、既然角色分工很清除了、接下来就是设计、与组装了。
5、考虑到可扩展性、低耦合、灵活性、和对扩展开放、对修改关闭的原则和面向接口编程、我们下面具体话类的设计。
6、根据角色我们可以抽象出三个接口:
a) 所有被观察者的接口——Subject;
b) 所有观察者的接口——Observer;
c) 显示信息的接口——DisplayElement;
7、对于Subject当然要拥有三个关于操作Observer的方法、注册、移除、和通知Observer的方法(观察者肯定要和被观察者结合起来)。
8、对于Observer肯定要有一个update方法、就是一旦检测到Subject有变动、就更新信息、所以还要实现DisplayElement接口。
二:具体实现
1、设计Subject接口:
package com.chy.dp.observer; public interface Subject { public void registerObserver(Observer observer); public void removeObserver(Observer observer); public void notifyObserver(); }
2、设计Observer接口:
package com.chy.dp.observer; public interface Observer { public void update(float temp, float humidity, float pressure); }
3、设计DisplayElement接口:
package com.chy.dp.observer; public interface DisplayElement { public void display(); }
4、设计具体的实现Subject接口的被观察者——WeatherDate
package com.chy.dp.observer; import java.util.ArrayList; import java.util.List; public class WeatherDate implements Subject { // Observer数组 可以简单想一下为什么不用LinkedList private List<Observer> observers = new ArrayList<Observer>(); private float temperature; private float humidity; private float pressure; @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(observer); } } @Override public void notifyObserver() { for (int i = 0; i < observers.size(); i++) { Observer o = (Observer) observers.get(i); o.update(temperature, humidity, pressure); } } /** * 模仿数据变动时自动触发notifyObserver函数 */ public void measurementsChanged() { notifyObserver(); } /** * 模仿数据变动、即当我们调用这个方法时就说明数据有改变、 这样所有的观察者都会被通知 */ public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } }
5、接下来就是实现我们的观察者Observer的具体实现——CurrentConditionDisplay:
package com.chy.dp.observer; public class CurrentConditionDisplay implements DisplayElement, Observer { private float temperature; private float humidity; private Subject weatherDate; /** * 将被观察者通过构造方法传递进来、并将此观察者注册到被观察者中 * 这样观察者和被观察者就完美的结合了 * @param weatherDate */ public CurrentConditionDisplay(Subject weatherDate) { super(); this.weatherDate = weatherDate; this.weatherDate.registerObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; display(); } @Override public void display() { System.out.println("Current comditions : " + temperature + " F degrees and " + humidity + "% humidity"); } }
6、同样的、关于显示ForecastConditionDisplay、StatisticsDisplay代码与上面非常相似。
7、Client:
package com.chy.dp.observer; @SuppressWarnings("unused") public class Client { public static void main(String[] args) { WeatherDate subject = new WeatherDate(); ForecastConditionDisplay fcd = new ForecastConditionDisplay(subject); CurrentConditionDisplay ccd = new CurrentConditionDisplay(subject); //改变被观察者的状态、观察者做出显示调整 subject.setMeasurements(19, 40, 20); } }
四:总结与补充
1、总结:
观察者模式的核心是先分清角色、定位好观察者和被观察者、他们是多对一的关系。实现的关键是要建立观察者和被观察者之间的联系、比如在被观察者类中有个集合是用于存放观察者的、当被检测的东西发生改变的时候就要通知所有观察者。在被观察者的构造方法中要传入他所要观察的对象、目的是将自己注册到被观察者持有的名单中、这样才能让被观察者及时的通知观察者关系的状态已经改变、并且调用观察者通用的方法将变化传递过去。
2、补充:
没什么好补充的了、设计模式神似的原因就是他们归根到底是建立在一些最基本的设计原则上的。要实现的最终目的就是让架构flexible、extensive、strong。这样是评定一个项目好坏的唯一标准。
更多内容:Java设计模式之起始