ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
本程序改编自《Head First Design Patterns》上关于气象站的例子,我将例子进行了化简。 总共7个java源文件 一个Subject接口 一个Observer接口 一个DisplayElement接口 一个Subject具体类,实现Subject接口 两个Observer具体类,实现Observer和DisplayElement接口 一个Main类,用于测试 观察者模式类图 ![这里写图片描述](https://box.kancloud.cn/2016-02-22_56cab10ce9590.jpg "") 图:我用插件画的类图 The Observation Pattern defines a one-to-many dependency。这里的one就是Subject, many就是Observers;one就是发布者,many就是订阅者;one就是事件源,many就是监听者。 # Talk is cheap, show me the code ### 一个Subject接口 ~~~ package observer; public interface Subject { public void registerObserver(Observer o); public void removeOvserver(Observer o); public void notifyObservers(); } ~~~ ### 一个Observer接口 ~~~ package observer; public interface Observer { public void update(double temperature, double humidity, String condition); } ~~~ ### 一个DisplayElement接口 这个接口只有display( )一个抽象方法,其实如果放在Observer里面的话,仅从代码层面考虑的话,也可以。但是如果从逻辑层面考虑的话,最好还是分开。毕竟这个display和观察者是”两码事“,不要耦合在一起。《Head First Design Pattern》上面的例子代码这样写,是为了描绘出一个生动形象的”气象站“的例子,来帮助大家理解观察者模式。 ~~~ package observer; public interface DisplayElement { public void display(); } ~~~ ### 一个Subject具体类 ~~~ package observer; import java.util.ArrayList; // Subject收集天气信息,然后通知Observers public class WeatherData implements Subject { private ArrayList<Observer> observers; private double temperature; private double humidity; private String condition; public WeatherData() { observers = new ArrayList<Observer>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeOvserver(Observer o) { int i = observers.indexOf(o); if(i>=0) { observers.remove(i); } } @Override public void notifyObservers() { for(Observer observer : observers) { observer.update(temperature, humidity, condition); } } public void measurementsChanged() { notifyObservers(); } public void setMeasurements(double temperature, double humidity, String condition) { this.temperature = temperature; this.humidity = humidity; this.condition = condition; measurementsChanged(); } public double getTemperature() { return temperature; } public double getHumidity() { return humidity; } public String getPressure() { return condition; } } ~~~ ### 两个Observer具体类 ~~~ package observer; // 显示当前天气情况 public class CurrentDisplay implements Observer, DisplayElement { private double temperature; private double humidity; private String condition; public CurrentDisplay(Subject weatherData) { weatherData.registerObserver(this); } @Override public void display() { System.out.println("Current Conditions: "+ temperature + " C degrees and " + humidity + "% humidity " + condition ); } @Override public void update(double temperature, double humidity, String condition) { this.temperature = temperature; this.humidity = humidity; this.condition = condition; display(); } public String toString() { return "This is Current Condition Display"; } } ~~~ ~~~ package observer; // 显示历史统计数据 public class StatisticDisplay implements Observer, DisplayElement { private double maxTemperature = 0; private double minTemperature = 100; private double temperatureSum = 0; private int totalUpdating = 0; public StatisticDisplay(Subject weatherData) { weatherData.registerObserver(this); } @Override public void display() { System.out.println("Avg/Max/Min temperture: " + temperatureSum/totalUpdating + "/" + maxTemperature + "/" + minTemperature); } @Override public void update(double temperature, double humidity, String condition) { temperatureSum += temperature; totalUpdating++; if(temperature > maxTemperature) { maxTemperature = temperature; } if(temperature < minTemperature) { minTemperature = temperature; } display(); } public String toString() { return "This is Statictic Display"; } } ~~~ ### 一个Main测试类 ~~~ package observer; public class Main { public static void main(String[] args) { WeatherData weatherStation = new WeatherData(); // 一个气象站作为Subject CurrentDisplay currentDisplay = new CurrentDisplay(weatherStation); // 注册Observer1 System.out.println(currentDisplay); // 这个是为了不让eclipse报出warning StatisticDisplay statisticDisplay = new StatisticDisplay(weatherStation); // 注册Observer2 System.out.println(statisticDisplay); // 这个是为了不让eclipse报出warning weatherStation.setMeasurements(20, 30, "晴天"); // 气象站测量出了新的天气,在第一时间通知所有的观察者 weatherStation.setMeasurements(16, 50, "多云"); weatherStation.setMeasurements(12, 60, "大风"); weatherStation.setMeasurements(12, 90, "小雨"); weatherStation.setMeasurements(11, 98, "大雨"); } } ~~~ # 运行结果 直接从eclipse复制过来 ~~~ This is Current Condition Display This is Statictic Display Current Conditions: 20.0 C degrees and 30.0% humidity 晴天 Avg/Max/Min temperture: 20.0/20.0/20.0 Current Conditions: 16.0 C degrees and 50.0% humidity 多云 Avg/Max/Min temperture: 18.0/20.0/16.0 Current Conditions: 12.0 C degrees and 60.0% humidity 大风 Avg/Max/Min temperture: 16.0/20.0/12.0 Current Conditions: 12.0 C degrees and 90.0% humidity 小雨 Avg/Max/Min temperture: 15.0/20.0/12.0 Current Conditions: 11.0 C degrees and 98.0% humidity 大雨 Avg/Max/Min temperture: 14.2/20.0/11.0 ~~~ # 总结 这个观察者模式,多个观察者(Observer)“围观”一个被观察者(Subject),被观察者是万众瞩目的焦点。观察者如果想得到Subject的“观察权”,首先需要向Subject **申请注册**,如果惹Subject生气了,不让你观察了,就把你**踢开**。Subject一旦发生了变化,就立刻**通知**所有观察者,引起观察者的**更新**。 黑体字就是Subject接口中的三个和Observer接口中的一个关键的抽象方法,必须实现: 申请注册:registerObserver() 踢开      :removeObserver() 通知      :notifyObservers() 更新      :update() 值得注意的是:update()方法传递的参数,可以传递整个subject对象,也可以只传递部分有用的数据成员。实验楼网站课程中的例子就是传递整个subject对象,《Head First Design Pattern》就是传递部分有用的数据成员。无论无何,最关键的是: Subject对象中的数据成员发生改变 → 触发Subject.notifyObservers()方法 → 触发所有Observer.update() 方法。 万变离不开这个链条。 Java中有一个java.util.Observable类(被观察者,Subject);还有一个java.util.Observer接口(观察者,Obserer)。看来观察者模式很常用,以至于都写进到java类库里面去了。 [更多设计模式,在新标签页中打开这里](http://blog.csdn.net/u013390476/article/details/50333763)