本书中的例子,持续采集气象数据(温度、湿度、气压),当采集的数据更新时,相应的当前气温、气温统计和预测气温这三个布告板也要进行更新。在不了解观察者模式前,一般通常的想法是在气象数据类中添加update当前气温、气温统计和预测气温这三个对象的update和display方法。这是基于实现的编程,不利于代码在扩展和维护时添加和删除布告板。
观察者模式有两种实现方式,一种是subject推送信息给观察者;另一种是observer主动向subject索要信息。第一种较为多用。观察者模式较为突出的特点在于实现了主题和观察者之间的
松耦合(
交换的对象尽可能设计为松耦合形式)。下面就具体来讲讲它的精华所在。
继续使用上面的例子,气象数据是subject,当前气温、气温统计和预测气温是三个观察者。subject会维护一个观察者的list,在创建观察者对象时,它会订阅subject。当object满足更新条件时(这里有一个
setchange方法,作为是否更新的开关,使发送通知更有弹性,eg.当检测到的气温(object)变化超过0.5度时才notify),subject会通知所有观察者,观察者会利用得到的数据进行update。当观察者不想再订阅subject时,它会通知subject将它删除,观察者内部有一个subject的指针,通过它实现添加和删除观察者。subject和observer之间拥有对方对象的指针,从而将耦合度降到最低。主题不需要知道观察者如何update,也不需要指定观察者,它可以自动通知所有的观察者。同时,观察者可以自己决定是否订阅,支持运行时的add和delete订阅,这样的设计就更加灵活和有弹性了。
以上是第一种设计方式,观察者模式第二种设计方式与第一种的不同之处在于观察者在接到通知后,它可以get主题的内容,而不只是一味地接收主题内容。subject在notify时会添加一个签名,传入主题内容object,之后调用各个观察者的update方法,这个方法多了这两个参数,一个是Subject*让观察者知道是哪个主题的通知,另一个是Object传递的数据对象。观察者可以决定这个主题的通知是否接收,接收的话,可以从object中get到自己需要的subject数据,实现了观察者有选择的接收数据。
第一种:push推送 Update(Object arg);
Update(float temperature, float humidity)
{
m_temperature = temperature;
m_humidity = humidity;
}
第二种:pull拉 Update(Subject *subject, Object arg);
Update(Subject *subject, Object arg)
{
if(subject instanceof WeatherData)
{
WeatherData *weatherData = (WeatherData *)subject;
m_temperature = weatherData.getTemperature();
m_humidity = weatherData.getHumidity();
Display();
}
}
client:
// 创建subject
WeatherData *weatherData = new WeatherData();
// 创建observer,并订阅subject
CurrentConditionDisplay *condition = new CurrentConditionDisplay(weatherData);
StatisticDisplay *statistic = new StatisticDisplay(weatherData);
ForecastDisplay *forecast = new ForecastDisplay(weatherData);
// subject更新object,(模拟更新数据)
weatherData.setMeasurements(123, 234.2, 345);
// subject推送主题, 内部会遍历observer list, 更新observer->update()
weatherData.Notify();