设计模式 之 观察者模式

我们来看一个例子

有一个天气预报的应用,主要负责将从别处获取到的天气信息(温度湿度气压等)显示到三个不同的布告板上,这三个布告板分别展示当前的天气信息天气的统计信息(从开始到当前的各天气因素的平均值)和当前的天气预报(根据天气因素判断当前的天气情况)。在这个例子中,我们不关心怎样得到天气信息,我们只关心将现成的天气信息进行处理之后发布到布告板上。另外,我们还需要保证以后可以灵活的添加或删除布告板。

针对以上需求,我们可以用观察者模式来解决这个问题。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。在观察者模式中主要有两个群体:观察者(Subject)和订阅者(Observer)。

就像订阅报纸一样,我事先去报社订阅《中国日报》,交上订阅费之后,报社就把我加到《中国日报》的订阅者列表中,而我并不需要关心报纸什么时候到报社,只需要继续回家写我的代码就可以了。当今天的报纸运达报社之后,报社就派出邮递员,按照订阅者列表,挨家挨户敲门,把报纸递到订阅者的手中(这其中当然也包括我)。这样,我只需要在听到敲门声的时候起身去取报纸就可以了。这就是一个典型的观察者模式的例子。

我们回到“天气预报”的例子,可以看出来,天气预报例子中的“气象站”就相当于报纸例子中的“报社”,天气预报例子中的“天气信息”就相当于报社例子中的“报纸”,而天气预报例子中的“布告板”就相当于报纸例子中的“订阅者”。现在我们主要需要解决以下几个问题:
1)建立一个“报社”,一有“报纸”送到,就开始派送
2)“报社”要保证所有的“订阅者”都能准时收到“报纸”
3)“报社”必须要有一个“订阅者列表”,这样才可以避免“误发”

有了需要解决的问题列表,我们就要开始考虑如何来解决这个问题了。其实,我对观察者模式的总结就是四个字:触发联动。当“报纸”运达“报社”时,触发了“报社”的“派送报纸”指令,从而导致了各个“订阅者”所受到的“报纸”信息的联动(信息刷新),而这就是本问题的解题思路。

要解决这个天气预报的问题(先暂时认为只有三个布告板),我们除了需要主函数MainClass之外,还需要五个类,具体如下:
1)观察者类WeatherSubject,它负责把得到的天气信息(温度、湿度、气压等)等信息“触发”给各个订阅者
2)订阅者父类Observer,它将订阅者的一些共有的属性和方法整合到这个类中,所有的订阅者都继承自这个类
3)三个“订阅者”(布告板),分别是:当前天气信息布告板CurrentObserver类、天气信息统计布告板EstimateObserver类和天气预报布告板GenerateObserver类
以下是五个类之间的类关系图:

设计模式 之 观察者模式_第1张图片

好啦,说了这么多,是时候进入Code Time(代码时间)啦,以下是代码~~~~

观察者类WeatherSubject:

 1 public class WeatherSubject {
 2     // 订阅者列表
 3     private List<Observer> observers = new ArrayList<Observer>();
 4     // 天气信息
 5     private double temperature; // 温度
 6     private double humidity; // 湿度
 7     private double pressure; // 气压
 8 
 9     // 将一个订阅者对象添加到订阅者列表中
10     public void registerObserver(Observer observer) {
11         observers.add(observer);
12     }
13 
14     // 将一个订阅者从订阅者列表中删除出去
15     public void removeObserver(Observer observer) {
16         observers.remove(observer);
17     }
18 
19     // 将信息的更新通知给所有的订阅者
20     private void notifyAllObservers() {
21         for (Observer observer : observers) {
22             observer.update(this.temperature, this.humidity, this.pressure);
23         }
24     }
25 
26     // 模拟的方法:将在别处获取的天气信息引入到系统中
27     public void setArguments(double temperature, double humidity,
28             double pressure) {
29         this.temperature = temperature;
30         this.humidity = humidity;
31         this.pressure = pressure;
32         notifyAllObservers();
33     }
34 }

订阅者父类Observer:

 1 public abstract class Observer {
 2     // 订阅者的名字(布告板的名字)
 3     protected String observerName;
 4     // 从观察者传过来的天气信息
 5     protected double temperature;
 6     protected double humidity;
 7     protected double pressure;
 8 
 9     // 抽象方法,更新各个布告板中的天气信息
10     public abstract void update(double temperature, double humidity,
11             double pressure);
12 
13     // 抽象方法,将天气信息经过处理之后显示到布告板上
14     public abstract void display();
15 }

当前天气信息布告板CurrentObserver:

 1 public class CurrentObserver extends Observer {
 2     public CurrentObserver() {
 3         this.observerName = "当前情况布告板";
 4     }
 5 
 6     public void update(double temperature, double humidity, double pressure) {
 7         this.temperature = temperature;
 8         this.humidity = humidity;
 9         this.pressure = pressure;
10         display();
11     }
12 
13     @Override
14     public void display() {
15         System.out.println(this.observerName + "上的信息有了更新:");
16         System.out.println("当前温度:" + this.temperature);
17         System.out.println("当前湿度:" + this.humidity);
18         System.out.println("当前气压:" + this.pressure);
19         System.out.println();
20     }
21 }

天气信息统计布告板EstimateObserver:

 1 public class EstimateObserver extends Observer {
 2     // 由于要算平均值,所以定义了各个天气信息的总和变量
 3     private double totleTemperature;
 4     private double totleHumidity;
 5     private double totlePressure;
 6     // 记录到当前为止总共更新了几次天气信息
 7     private double times = 1;
 8 
 9     public EstimateObserver() {
10         this.observerName = "天气统计布告板";
11     }
12 
13     public void update(double temperature, double humidity, double pressure) {
14         this.totleTemperature += temperature;
15         this.totleHumidity += humidity;
16         this.totlePressure += pressure;
17         display();
18         times++;
19     }
20 
21     @Override
22     public void display() {
23         System.out.println(this.observerName + "上的信息有了更新:");
24         System.out.println("平均温度:" + this.totleTemperature / times);
25         System.out.println("平均湿度:" + this.totleHumidity / times);
26         System.out.println("平均气压:" + this.totlePressure / times);
27         System.out.println();
28     }
29 }

天气预报布告板GenerateObserver:

 1 public class GenerateObserver extends Observer {
 2     public GenerateObserver() {
 3         this.observerName = "天气预报布告板";
 4     }
 5 
 6     public void update(double temperature, double humidity, double pressure) {
 7         this.temperature = temperature;
 8         this.humidity = humidity;
 9         this.pressure = pressure;
10         display();
11     }
12 
13     @Override
14     public void display() {
15         System.out.println(this.observerName + "上的信息有了更新:");
16         // 根据天气信息判断当前的天气状况
17         // 额...LZ对天气实在是所知不多,这些个条件都是我自己编的,如果不对(好吧肯定不对)请不要讨厌我 (=^ω^=)
18         String weather = "晴";
19         if (this.temperature < -5 && this.humidity > 50) {
20             weather = "大雪";
21         }
22         System.out.println("当前天气:" + weather);
23         System.out.println();
24     }
25 }

最后就是主函数MainClass了:

 1 public class MainClass {
 2     public static void main(String[] args) {
 3         Scanner scanner = new Scanner(System.in);
 4         // 定义观察者对象
 5         WeatherSubject subject = new WeatherSubject();
 6         // 定义订阅者对象,并加入到订阅者列表中
 7         Observer observer1 = new CurrentObserver();
 8         subject.registerObserver(observer1);
 9         Observer observer2 = new EstimateObserver();
10         subject.registerObserver(observer2);
11         Observer observer3 = new GenerateObserver();
12         subject.registerObserver(observer3);
13         // 连续输入三次天气信息,三个布告板中都会联动的更新数据
14         for (int i = 0; i < 3; i++) {
15             double temperature = scanner.nextDouble();
16             double humidity = scanner.nextDouble();
17             double pressure = scanner.nextDouble();
18             subject.setArguments(temperature, humidity, pressure);
19         }
20     }
21 }

以下是运行结果:
设计模式 之 观察者模式_第2张图片

你可能感兴趣的:(设计模式 之 观察者模式)