观察者模式
定义:观察者模式定义了对象之间的一对多依赖,这样一来当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
假如我们有一个报社,和一些客户。我是其中的一个客户,报社每当一有报纸要出版就会给所有订阅过报纸的客户进行送报。客户如果取消订报服务的话,报社就会将客户从订阅名单中删除,之后这个客户就不会再收到报纸了。
松耦合的威力
当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。观察者模式提供了一种对象设计,让主题与观察者之间松耦合。
为什么呢?
关于观察者的一切,主题只知道观察者实现了某个接口(也就是oberver接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节。
任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以我们可以随时增加观察者。事实上,在运行时我们可以用新的观察者取代现有的观察者,主题不会受到任何影响。同样的也可以在任何时候删除某些观察者。
有新类型的观察者出现时,主题的代码也不需要修改。假如我们有个新的具体类需要当观察者,我们不需要为了兼容新类型而修改主题的代码,所有要做的就是在新类型里实现观察者接口,然后注册为观察者即可。主题不在乎别的,它只会发送通知给所有实现了观察者接口的对象。
我们可以独立地复用主题或观察者。如果我们要在其他地方需要使用主题或观察者,可以轻易地复用,因为二者并非紧耦合。
改变主题或观察者其中一方,并不会影响另一方。因为两者是松耦合的,所以只要他们之间的接口仍被遵守,我们就可以自由地改变他们。
这里又收到一个设计原则:为了交互对象之间的松耦合设计而努力。
松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
#include <vector>
#include <algorithm>
using namespace std;
class Observer
{
public:
virtual void update(float temp,float humidity,float pressure)=0;
};
class Subject
{
public:
virtual void registerObserver(Observer* o) = 0;
virtual void removeObserver(Observer* o) = 0;
virtual void notifyObeserver() = 0;
void MeasureMentChanged();
};
class WeatherData :public Subject {
private:
vector<Observer*> *observers;
float tempature;
float humidity;
float pressure;
public:
WeatherData()
{
observers = new vector<Observer*>();
}
void registerObserver(Observer* o)
{
observers->push_back(o);
}
void removeObserver(Observer* o)
{
//等会看看effective stl
auto res=find(observers->begin(), observers->end(), o);
if (res!=observers->end())
observers->erase(res);
}
void notifyObeserver()
{
for (vector<Observer*>::size_type i = 0; i < observers->size(); i++)
{
(*observers)[i]->update(tempature, humidity, pressure);
}
}
void MeasureMentChanged()
{
notifyObeserver();
}
void set_Changed(float tempature, float humidity, float pressure)
{//此方法只是用来测试用,实际项目中这些数据是从其他接口获得
this->tempature = tempature;
this->humidity = humidity;
this->pressure = pressure;
MeasureMentChanged();
}
};
class DisplayElement
{
public:
virtual void Diaplay() = 0;
};
class CurrentConditionDisplay :public Observer, public DisplayElement
{
private:
Subject* weatherData;//定义一个这样的成员变量的目的是 方便自由取消注册
float temerature;
float humidity;
float weatherData;
CurrentConditionDisplay(Subject* weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void update(float temperature, float humidity, float pressure)
{
this->temerature = temperature;
this->humidity = humidity;
Diaplay();
}
void Diaplay()
{
//打印气象信息
}
};
总结:
1.每一个观察者都有一个对主题的引用。这是为了便于以后如果我们要取消注册的话,可以直接通过这个引用操作。
2.观察者有两种形式:
①推
当数据到位就立刻推给所有的观察者。
②拉
当数据到位时,只通知观察者,观察者根据需求自己去拉数据。所以主题要提供一个访问所有数据的接口。
3.我们还可以将主题设计一个状态位,用一个setChanged方法修改这个状态位,好让让notifyObeserver知道当他被调用时应该通知观察者。如果调用notifyObserver之前没有先调用setChanged(),观察者就不会被通知。这样做有其必要性,setchanged方法可以让你在更新观察者时,有更多的弹性,你可以更适当地通知观察者。比方说,如果没有setChanged(),我们的气象站测量是如此敏锐,以以致于温度计读数每十分之一就会更新。这会造成了WeatherData对象持续不断地通知观察者,我们并不希望看到这样的事情发生。如果我们希望半度以上才更新,就可以在温度差距到达半度时,调用setChanged(),进行有效的更新。