观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新(是不是感觉很神奇!!!)。
小A公司接了一个为气象站开发Internet气象观测站的项目。项目开发要求:由气象站提供一个WeatherData对象负责追踪目前的天气状况(温度、湿度、气压),要求公司开发一个应用,有三种布告板,分别及时更新显示目前的状况,而且这个应用能够易于扩展开发新的布告板。
现在我们开始开发吧
1.第一次尝试
class WeatherData { public void measurementsChanged() {
//取得最新测量值 float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); //更新到各个布告板 CurrentConditionsDisplay.update(temp, humidity, pressure); statisticsDisplay.update(temp, humidity, pressure); forecastDisplay.update(temp, humidity, pressure); } //这里是其他WeatherData方法 }
解析下:代码看上去蛮漂亮的,但是想想我们的设计原则
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2.针对接口编程,而不是针对实现编程。
2.第二次尝试:观察者模式
观察者模式主要的两个对象是主题和观察者,观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格观察者可能因此新值而更新。
接下来我们来看下观察者模式的一种常见做法:Subject和Observer接口的类设计的做法。
public interface Subject { void registerObserver(Observer o);//以观察者作为变量,该观察者是用来注册的。 void removeObserver(Observer o);//以观察者作为变量,该观察者是用来删除的。 void notifyObservers();//当主题状态改变时,这个方法会被调用,以通知所有观察者。 }
public interface Observer { void update(float temp,float humidity,float pressure);//所有观察者都必须实现updata()方法,以实现观察者接口。 }
class WeatherData:Subject {
//ArrayList 来记录依赖主题的所有观察者 private ArrayList observers; private float temperature; private float humidity; private float pressure; public WeatherData() { observers = new ArrayList(); } #region Subject 成员 //当注册观察者时,我们只要把它加到ArrayList的后面即可。 public void registerObserver(Observer o) { observers.Add(o ); } //当删除观察者时,我们只要把它从ArrayList中删除即可。 public void removeObserver(Observer o) { int i = observers.IndexOf(o ); if (i >=0) { observers.Remove(i ); } } //有趣的地方来了!在这里,我们把状态告诉每一个观察者。
//因为观察者都实现了update(),所以我们知道如何通知它们。 public void notifyObservers() { for (int i = observers.Count-1;i >=0 ;i -- ) { Observer observer = (Observer)observers[i]; observer.update(temperature ,humidity ,pressure ); } }
//当从气象站得到更新观测值时,我们通知观察者。 public void measurementsChanged() { notifyObservers(); }
// 条件所限,所以我们用这个方法来手动更新观测值。 public void setMeasurements(float temperature,float humidity,float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } #endregion }
public interface DisplayElement
{
void display();//当布告板显示时,调用这个方法。
}
//这里我就写了一个布告板,有兴趣的朋友可以自己多写几个布告板,体验下观察者模式的精髓。
class CurrentConditionsDisplay:Observer ,DisplayElement { private float temperature; private float humidity; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData)//构造器需要 weatherData对象,作为注册之用。 { this.weatherData = weatherData; weatherData.registerObserver(this ); } #region DisplayElement 成员 //把最近的温度和湿度显示出来 public void display() { Console.WriteLine("Current conditions:"+temperature + "F degrees and"+humidity +"% humidity"); } #endregion #region Observer 成员 //当update()被调用时,我们把温度和湿度保存起来,然后调用display(). public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } #endregion }
测试下:
static void Main(string[] args) { WeatherData weatherData = new WeatherData();//首先,建立一个WeatherData对象
//建立一个布告板并把WeatherData对象传给它,currentDisplay 在构造器中往WeatherData对象注册自己 CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData );
//模拟新的气象测量 weatherData.setMeasurements(80,65,30.4f); weatherData.setMeasurements(82,70,29.2f); weatherData.setMeasurements(78,90,29.2f); Console.ReadKey(); }
调试显示: