设计模式(2)--观察者模式
什么是观察者模式
举个例子来简单说明下这个模式:假如现在你在一家报社订阅了报纸,每当有新的期刊,那么他们就会把报纸送到你家,如果你什么时候不想看这一期刊的时候,你就可以取消订阅,那么这时候他们就不会将报纸送到你家了。这其实就是利用了观察者模式,先给出两个基本概念:主题,就相当于被观察的对象,这里指的就是报社;观察者,实时接收主题新的数据,这里指的就是订阅报纸的人。
深入理解观察者模式
先给出一个问题,然后我们就根据这个问题进行讨论。
问题:现在有个气象站,给一些气象显示器实时发送数据,气象显示器所显示的数据方式不一样,有些可能着重温度或者湿度,客户也可以根据自己的需求来开发一份气象显示器。
根据这个问题,你的代码可能是这样的:
以下代码不是一个文件
//TemperatureDisplay.java public class TemperatureDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); } } //HumidityDisplay.java public class HumidityDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("humidity is " + humidity); } } //MixDisplay.java public class MixDisplay { public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); System.out.println("humidity is " + humidity); System.out.println("pressure is " + pressure); } } //WeatherData.java public class WeatherData { private float temperature; private float humidity; private float pressure; private TemperatureDisplay temperatureDisplay; private HumidityDisplay humidityDisplay; private MixDisplay mixDisplay; public WeatherData() { } public WeatherData(TemperatureDisplay temperatureDisplay, HumidityDisplay humidityDisplay, MixDisplay mixDisplay) { super(); this.temperatureDisplay = temperatureDisplay; this.humidityDisplay = humidityDisplay; this.mixDisplay = mixDisplay; } public float getTemperature() { return temperature; } public void setTemperature(float temperature) { this.temperature = temperature; } public float getHumidity() { return humidity; } public void setHumidity(float humidity) { this.humidity = humidity; } public float getPressure() { return pressure; } public void setPressure(float pressure) { this.pressure = pressure; } //当气象数据变更时调用 public void measurementsChange() { temperatureDisplay.update(temperature, humidity, pressure); humidityDisplay.update(temperature, humidity, pressure); mixDisplay.update(temperature, humidity, pressure); } }
这可能是你最初的想法,或许你的想法比这个更好
我们来仔细看下上面的代码,你会发现这个代码的扩展性很差。如果有人想自定义一个气象显示器,那么你必须修改WeatherData这个类中的代码,显然这个代码是不可以让其他人随便进行修改的,因为这个是气象站提供的。所以气象站的设计人员肯定就需要重构这一代码。
解决方案:
我们发现这个气象站就好像是主题一样,而气象显示器就貌似是观察者。你们是怎么看的呢?
看看气象显示器的代码,发现都只有一个update方法,当然你可以添加其他的方法,那么我们就可以定义一个接口Display,让所有的气象显示器都实现这个接口。然后在WeatherData类中添加一个集合属性,这个集合中包含的是Display这一类型的对象。
为了可扩展性,可能有很多气象站,所以我们定义一个Subject的接口,该接口中主要有三个方法,分别是注册气象显示器、删除注册气象显示器、通知注册气象显示器,即registerObverse、removeObverse、notifyObverse。而每个气象显示器中增加一个都必须有一个Subject类型的属性,并且在构造方法中需要传递一个Subject类型的参数进行实例化这个对象,然后这个气象显示器在这个主题中进行注册。
根据这个方案,示例代码如下:
//Subject.java public interface Subject { void registerObverse(); void removeObverse(); void notifyObverse(); } //Display.java //这就是一个观察者类的接口 public interface Display { void update(float temperature, float humidity, float pressure); } //WeatherData.java import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject { private float temperature; private float humidity; private float pressure; private List<Display> displays; public WeatherData() { displays = new ArrayList<>(); } public float getTemperature() { return temperature; } public void setTemperature(float temperature) { this.temperature = temperature; } public float getHumidity() { return humidity; } public void setHumidity(float humidity) { this.humidity = humidity; } public float getPressure() { return pressure; } public void setPressure(float pressure) { this.pressure = pressure; } @Override public void registerObverse(Display display) { displays.add(display); } @Override public void removeObverse(Display display) { int i = displays.indexOf(display); if(i>=0) displays.remove(display); } @Override public void notifyObverse() { for(Display display:displays){ display.update(temperature, humidity, pressure); } } //当气象数据变化时就修改调用这一方法 public void measurementsChanged(){ notifyObverse(); } } //MixDisplay.java //这里写了一个气象显示器,其他的跟着类似 public class MixDisplay implements Display { private Subject subject; public MixDisplay(Subject subject) { this.subject = subject; subject.registerObverse(this); } public void remove() { subject.removeObverse(this); } @Override public void update(float temperature, float humidity, float pressure) { System.out.println("temperature is " + temperature); System.out.println("humidity is " + humidity); System.out.println("pressure is " + pressure); } }
这时候如果你想自定义一个显示器,只需要实现Display这一接口就可以了,然后在气象显示器中定义一个Subject类型的属性,然后在构造是进行注册该观察者,那么只要气象数据变化了你就可以接受到。这是不是具有很好的可扩展性呢?
其实呢Java中内置中就包含了这一模式,其主题就是Observable(这是一个类),观察者则是Obverse(这是一个接口)。
所以你可以试试用内置的这一模式实现下以上案例。
如果你对该博客有什么见解或疑问,可以留言哦>v<