一、案例背景
贵公司获选为我公司建立下一代Internet气象观测站! 该气象站必须建立在我们专利申请中的WeatherData对象上,由WeatherData对象负责追踪目前的天气状况 (温度、 湿度、气压)。
我们希望贵公司能建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。 当WeatherObject对象获得最新的测量数据时, 三种布告板 必须实时更新。 而且,这是一个可以扩展的气象站,Weather -0-Rama气象 站希望公布 一组API,好让其他开发人员可以写出自己的 气象布告板,并插入此应用中。我们希望贵公司能提供这样的API。
二、案例分析
这是一种很常见的场景。WeatherObject对象相当于一个主题,管理了三个对象:温度、 湿度、气压。三种布告板需要订阅这个主题,以便拿到最新的数据并显示。
实现这些并不难,难的是你要未雨绸缪般预料到将来你的工作将会发生什么样的变化,好让自己在这些变化来临时,快速的去响应变化,从而少加点班。
你的工作是为对方设计良好的接口,在这里我们假设甲方提供的信息种类是固定的,你只需要审视自己负责的部分工作即可。首先,显而易见,主题的数量有可能发生变化:减少或增加。其次,甲方提供的信息是固定的,但是内容的表现形式也有可能变化。
三、代码实现
这里给出相关案例的C++代码实现:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//@brief: 观察者(抽象接口)
class Observer
{
public:
virtual void update(float temp, float humidity, float pressure) = 0;
};
//@brief: 主题(抽象接口)
class Subject
{
public:
virtual void registerObserver(Observer* observer) = 0;
virtual void removeObserver(Observer* observer) = 0;
virtual void notifyObservers() = 0;
};
//@brief: 表现形式(抽象接口)
class DisplayElement
{
public:
virtual void display() = 0;
};
//@brief: 主题(具体实现)
class WeatherData : public Subject
{
private:
vector<Observer*> observers {};
float temperature {};
float humidity {};
float pressure {};
public:
void registerObserver(Observer* observer) override
{
observers.push_back(observer);
}
void removeObserver(Observer* observer) override
{
auto it = find(observers.begin(), observers.end(), observer);
if (it != observers.end())
{
observers.erase(it);
}
}
void notifyObservers() override
{
for (auto observer : observers)
{
observer->update(temperature, humidity, pressure);
}
}
void measurementsChanged()
{
notifyObservers();
}
void setMeasurements(float temp, float humidity, float pressure)
{
this->temperature = temp;
this->humidity = humidity;
this->pressure = pressure;
measurementsChanged();
}
float getTemperature() const
{
return temperature;
}
float getHumidity() const
{
return humidity;
}
float getPressure() const
{
return pressure;
}
};
//@brief:观察者1号(具体实现)
class CurrentConditionsDisplay : public Observer, public DisplayElement
{
private:
float temperature {};
float humidity {};
float pressure {};
Subject* weatherData {};
public:
CurrentConditionsDisplay(Subject* weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void update(float temp, float humidity, float pressure) override
{
this->temperature = temp;
this->humidity = humidity;
this->pressure = pressure;
display();
}
void display() override
{
cout << "手机通知 -- 天气预报" << endl;
cout << "Current conditions: " << temperature << "°C, " << humidity << "%, " << pressure << " hPa" << endl;
}
};
// 观察者2号(具体实现)
class StatisticsConditionDisplay : public Observer, public DisplayElement
{
private:
float temp;
float humid;
float pressure;
Subject* weather {};
public:
StatisticsConditionDisplay(Subject* sub)
{
this->weather = sub;
weather->registerObserver(this);
}
void update(float temp, float humidity, float pressure) override
{
this->temp = temp;
this->humid = humidity;
this->pressure = pressure;
display();
}
void display()
{
cout << "电视节目 -- 天气预报" << endl;
cout << "今日温度: " << temp << endl;
cout << "今日湿度: " << humid << endl;
cout << "今日气压: " << pressure << endl;
}
};
//测试代码
int main()
{
WeatherData* weatherData = new WeatherData;
CurrentConditionsDisplay currentDisplay { weatherData };
StatisticsConditionDisplay statisticsDisplay { weatherData };
weatherData->setMeasurements(80, 65, 30.4f);
cout << endl << endl;
weatherData->setMeasurements(82, 70, 29.2f);
return 0;
}