观察者模式

观察者模式

这种模式大家可能叫它“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) {
    // ...
  }
};

这上述代码的问题在哪里呢?
其实我们想想其实功能已经实现了,但是呢?

  1. 如果我不需要进度条这个功能,上面的代码能编译过吗。不能,因为ImageHandler已经和ProgressBar耦合在一起了。这违背了什么原则呢?
    上层模块不应该依赖低层模块的实现,而应该依赖于抽象。
  2. 如果我下次不是想要进度条,我是想将这个数据sink到日志文件系统中,或者在终端展示出来,难不成还有写几个类似的代码放在一起。
  3. 如果同时支持多种模式,展示处理进度。那上面的代码应该怎么改呢。

首先依赖于抽象,那么我们的代码就可以改成这样

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 {
    // ...
  }
};

最后看看定义

观察者模式定义了一对多(变化的)的依赖关系,以便当一个对象的状态发生改变的时候,所有依赖该对象的订阅者都得到通知,自动更新。

要点

  1. 使用观察者模式,我们可以独立的改变目标和观察者,使得两者之间的关系达到松耦合。
  2. 目标的状态更新时(发布通知时),不需要指定特定的观察者,消息会自动传播。
  3. 由观察者自己决定是否关注对象,目标对象不需要知道有那些观察者正在观察它。

最后说观察者模式非常常见,各式各样的,但是其实这里的知识点其实很类似,重要的就是多态的运用。在UI开发中的mvc,流媒体的gstreamer等等项目中都有使用。

posted @ 2020-11-09 12:02  cyssmile  阅读(81)  评论(0编辑  收藏  举报