观察者模式
类图
定义
定义了对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又被称为发布-订阅(Publish/Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式,或从属者(Dependents)模式。观察者模式是一种对象行为型的模式。
优缺点
优点:
- 具体主题和具体观察者是松耦合关系。
由于主题(Subject)接口仅仅依赖于观察者(Observer)接口,因此具体主题只是知道它的观察者是实现观察者(Observer)接口的某个类的实例,但不需要知道具体是哪个类。同样,由于观察者仅仅依赖于主题(Subject)接口,因此具体观察者只是知道它依赖的主题是实现主题(subject)接口的某个类的实例,但不需要知道具体是哪个类。 - 观察者模式满足“开-闭原则”。
主题(Subject)接口仅仅依赖于观察者(Observer)接口,这样,我们就可以让创建具体主题的类也仅仅是依赖于观察者(Observer)接口,因此如果增加新的实现观察者(Observer)接口的类,不必修改创建具体主题的类的代码。同样,创建具体观察者的类仅仅依赖于主题(Observer)接口,如果增加新的实现主题(Subject)接口的类,也不必修改创建具体观察者类的代码。
缺点:
如果一个被观察者对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
如果在被观察者之间有循环依赖的话,给观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。
虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制是观察者知道所观察的对象是怎么发生变化的。
设计原则
在交互的对象之间争取松耦合设计
由于松耦合设计使得对象间的依赖最小化,所以,我们能够创建柔性的oo系统,应对变化的情况,因为对象间的依赖降到了最低。
代码
Subject类:
#ifndef SUBJECT_H
#define SUBJECT_H
#include "observer.h"
class Subject
{
public:
virtual void registerObserver(Observer *o) = 0;
virtual void removeObserver(Observer *o) = 0;
virtual void notifyObservers() = 0;
};
#endif // SUBJECT_H
Observer类:
#ifndef OBSERVER_H
#define OBSERVER_H
class Observer
{
public:
virtual void update(float temp, float humidity, float pressure) = 0;
};
#endif // OBSERVER_H
DisplayElement类:
#ifndef DISPLAYELEMENT_H
#define DISPLAYELEMENT_H
class DisplayElement
{
public:
virtual void display() = 0;
};
#endif // DISPLAYELEMENT_H
WeatherData类:
#ifndef WEATHERDATA_H
#define WEATHERDATA_H
#include "subject.h"
#include <QVector>
class WeatherData : public Subject
{
public:
WeatherData();
void registerObserver(Observer *o) override;
void removeObserver(Observer *o) override;
void notifyObservers() override;
void measurementsChanged();
void setMeasurements(float temperature, float humidity, float pressure);
private:
QVector<Observer *> observers;
float temperature;
float humidity;
float pressure;
};
#endif // WEATHERDATA_H
#include "weatherdata.h"
WeatherData::WeatherData()
{
}
void WeatherData::registerObserver(Observer *o)
{
observers.append(o);
}
void WeatherData::removeObserver(Observer *o)
{
int i = observers.indexOf(o);
if(i >= 0) {
observers.remove(i);
}
}
void WeatherData::notifyObservers()
{
for(int i = 0; i < observers.size(); i++) {
Observer *observer = observers.at(i);
observer->update(temperature, humidity, pressure);
}
}
void WeatherData::measurementsChanged()
{
notifyObservers();
}
void WeatherData::setMeasurements(float temperature, float humidity, float pressure)
{
this->temperature = temperature;
this->humidity = humidity;
this->pressure = pressure;
measurementsChanged();
}
CurrentConditionsDisplay类:
#ifndef CURRENTCONDITIONSDISPLAY_H
#define CURRENTCONDITIONSDISPLAY_H
#include "observer.h"
#include "displayelement.h"
#include "subject.h"
#include <QDebug>
class CurrentConditionsDisplay : public Observer, public DisplayElement
{
public:
CurrentConditionsDisplay(Subject *weatherData);
void update(float temperature, float humidity, float pressure) override;
void display() override;
private:
float temperature;
float humidity;
Subject *weatherData;
};
#endif // CURRENTCONDITIONSDISPLAY_H
#ifndef CURRENTCONDITIONSDISPLAY_H
#define CURRENTCONDITIONSDISPLAY_H
#include "observer.h"
#include "displayelement.h"
#include "subject.h"
#include <QDebug>
class CurrentConditionsDisplay : public Observer, public DisplayElement
{
public:
CurrentConditionsDisplay(Subject *weatherData);
void update(float temperature, float humidity, float pressure) override;
void display() override;
private:
float temperature;
float humidity;
Subject *weatherData;
};
#endif // CURRENTCONDITIONSDISPLAY_H
ForecastDisplay类:
#ifndef FORECASTDISPLAY_H
#define FORECASTDISPLAY_H
#include "observer.h"
#include "displayelement.h"
#include "subject.h"
#include <QDebug>
class ForecastDisplay : public Observer, public DisplayElement
{
public:
ForecastDisplay(Subject *weatherData);
void update(float temperature, float humidity, float pressure) override;
void display() override;
private:
float temperature;
float humidity;
Subject *weatherData;
};
#endif // FORECASTDISPLAY_H
#include "forecastdisplay.h"
ForecastDisplay::ForecastDisplay(Subject *weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void ForecastDisplay::update(float temperature, float humidity, float pressure)
{
this->temperature = temperature;
this->humidity = humidity;
display();
}
void ForecastDisplay::display()
{
qDebug() << "Forecast" << temperature << "F degress and"
<< humidity << "% humidity";
}
StatisicsDisplay类:
#ifndef STATISICSDISPLAY_H
#define STATISICSDISPLAY_H
#include "observer.h"
#include "displayelement.h"
#include "subject.h"
#include <QDebug>
class StatisicsDisplay : public Observer, public DisplayElement
{
public:
StatisicsDisplay(Subject *weatherData);
void update(float temperature, float humidity, float pressure) override;
void display() override;
private:
float temperature;
float humidity;
Subject *weatherData;
};
#endif // STATISICSDISPLAY_H
#include "statisicsdisplay.h"
StatisicsDisplay::StatisicsDisplay(Subject *weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void StatisicsDisplay::update(float temperature, float humidity, float pressure)
{
this->temperature = temperature;
this->humidity = humidity;
display();
}
void StatisicsDisplay::display()
{
qDebug() << "Statisics" << temperature << "F degress and"
<< humidity << "% humidity";
}
测试:
#include "mainwindow.h"
#include <weatherdata.h>
#include "currentconditionsdisplay.h"
#include "statisicsdisplay.h"
#include "forecastdisplay.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
WeatherData *weatherData = new WeatherData();
CurrentConditionsDisplay *currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisicsDisplay *statisicsDisplay = new StatisicsDisplay(weatherData);
ForecastDisplay *forecastDisplay = new ForecastDisplay(weatherData);
weatherData->setMeasurements(80, 65, 30.4);
weatherData->setMeasurements(82, 70, 29.2);
weatherData->setMeasurements(78, 90, 29.2);
}
MainWindow::~MainWindow()
{
}