观察者模式
观察者模式
这种模式大家可能叫它“observer/event模式” 或者"发布订阅模式"。
这种模式应用场景非常丰富,比如在项目中程序运行的性能监控,消息监控,日志信息处理,查看程序处理的进度。
在项目中运行一个程序时,通常有这样操作addWatcher(), removeWatcher(BaseWatcher* watcher)。其实看到这样的代码就可以知道这可能就是使用的观察者模式。
比如这样一个需求,我在使用一个模型处理某个数据集,但是这个数据集中的图片非常多。
class ImageHandler {
public:
return_type Process();
private:
uint32_t totalNum_; // number of image in this datasets
};
如何在上述代码中添加进度条呢,可能大家最初的想法,就是在ImageHandler中添加一个progressBar对象的指针
比如改成这样
class ImageHandler {
public:
return_type Process() {
// ...
if (!progressBar_) {
progressBar_->ShowStatus(value);
}
//...
}
private:
uint32_t totalNum_; // number of image in this datasets
ProgressBar* progressBar_ = nullptr;
};
class ProgressBar {
public:
void ShowStatus(float value) {
// ...
}
};
这上述代码的问题在哪里呢?
其实我们想想其实功能已经实现了,但是呢?
- 如果我不需要进度条这个功能,上面的代码能编译过吗。不能,因为ImageHandler已经和ProgressBar耦合在一起了。这违背了什么原则呢?
上层模块不应该依赖低层模块的实现,而应该依赖于抽象。 - 如果我下次不是想要进度条,我是想将这个数据sink到日志文件系统中,或者在终端展示出来,难不成还有写几个类似的代码放在一起。
- 如果同时支持多种模式,展示处理进度。那上面的代码应该怎么改呢。
首先依赖于抽象,那么我们的代码就可以改成这样
class BaseObserver {
public:
return_type ShowStatus (float value);
};
class ImageHandler {
public:
return_type Process() {
// ...
if (!progressBar_) {
progressBar_->ShowStatus(value);
}
//...
}
private:
uint32_t totalNum_; // number of image in this datasets
BaseObserverr* observer_ = nullptr;
};
class ProgressBar : public BaseObserver{
public:
void ShowStatus(float value) override {
// ...
}
};
那么像上述代码我们解决了第一个问题,上层的代码不依赖与下层代码。
那么我们还没实现同时支持多种观察者啊。所以继续将代码改进下
class BaseObserver {
public:
return_type ShowStatus (float value);
};
class ImageHandler {
public:
return_type Process() {
// ...
NotifyAll(value);
//...
}
bool AddObserver(BaseObserver* observer) {
observer_.push_back(observer);
return true;
}
bool RemoveObserver(BaseObserver* observer) {
observer_.remove(observer);
}
private:
void NotifyAll(float value) {
for (auto &it : observers_) {
it->ShowStatus(value);
}
}
private:
uint32_t totalNum_; // number of image in this datasets
vector<BaseObserverr>* observers_ ;
};
class ProgressBar : public BaseObserver{
public:
void ShowStatus(float value) override {
// ...
}
};
其实这已经比较像观察者模式,我们可以通过AddObserver和RemoveObserver来增加或者移除观察者,通过NotifyAll来将消息扩散给所有的观察者。
这里ImageHandler这个上层模块,就不依赖于低层的watcher,而且他也不知道通知给了那些订阅者。达到了解耦合的作用。
当然这里代码还可以改进,毕竟这里可能会增加或者删除观察者,那么可能数据结构可能不使用vector比较好,使用list更好。那么代码就可以改成
class BaseObserver {
public:
return_type ShowStatus (float value);
};
class ImageHandler {
public:
return_type Process() {
// ...
NotifyAll(value);
//...
}
bool AddObserver(BaseObserver* observer) {
observer_.push_back(observer);
return true;
}
bool RemoveObserver(BaseObserver* observer) {
for(auto iter = observer_.begin(); iter != observer_.end(); iter++) {
// ....
}
}
private:
void NotifyAll(float value) {
for (auto &it : observers_) {
it->ShowStatus(value);
}
}
private:
uint32_t totalNum_; // number of image in this datasets
list<BaseObserverr>* observers_ ;
};
class ProgressBar : public BaseObserver{
public:
void ShowStatus(float value) override {
// ...
}
};
最后看看定义
观察者模式定义了一对多(变化的)的依赖关系,以便当一个对象的状态发生改变的时候,所有依赖该对象的订阅者都得到通知,自动更新。
要点
- 使用观察者模式,我们可以独立的改变目标和观察者,使得两者之间的关系达到松耦合。
- 目标的状态更新时(发布通知时),不需要指定特定的观察者,消息会自动传播。
- 由观察者自己决定是否关注对象,目标对象不需要知道有那些观察者正在观察它。
最后说观察者模式非常常见,各式各样的,但是其实这里的知识点其实很类似,重要的就是多态的运用。在UI开发中的mvc,流媒体的gstreamer等等项目中都有使用。