第三章 观察模式

本文探讨了如何通过数据模型优化Observer-Subject设计,以解决Update接口硬编码的问题。通过将Subject和Observer解耦,实现更灵活的数据传递。实例展示了如何在不同场景下应用,如跨进程、跨线程和分布式系统中.

1. 观察模式

  • 考察报纸发行过程,一些家庭向报纸销售商进行了订阅,销售商定期把报纸放到家庭门口
    家庭购报纸的过程叫做注册过程
    销售商发行报纸的过程是推送过程

销售商和家庭之间是一对多的关系

2. 总体设计

注意事项:Observer 和 Subject 放在同一个进程中!!!
在这里插入图片描述

2.1 接口说明

registerObserver(Observer& O);
输入参数:观察者对象的引用。
函数功能:Subject负责把Observer 添加到订阅者列表中

removeObserver(Observer& O);
输入参数:观察者对象的引用
函数功能:Subject负责把Observer从订阅者列表中删除

notifyObservers(void);
函数功能:Subject负责更新所有订阅者的数据

update();
函数功能:Observer根据输入数据完成数据更新

2.2 设计方案1:Observer 和 Subject

在这里插入图片描述

2.2.1 方案说明

该设计存在多项缺陷。

  • Subject 和 Observer接口设计原则上应该彼此独立。但是update接口进行了Hard Code。
    void update (float temp, float humi, float pressure);更好的方法将在2.2小节中说明
    在这里插入图片描述

2.2.2 实现代码

#include <iostream>
#include <list>
using namespace std;

class DisplayElement
{
public:
  virtual void display (void) = 0;
};

class Observer
{
public:
  virtual void update (float temp, float humi, float pressure) = 0;
};

class ForcastDisplay:public DisplayElement, public Observer
{
public:
  ForcastDisplay (void)
  {
    m_temperature = 0;
    m_pressure = 0;
    m_humidity = 0;
    m_future = 0;
  }


  void update (float temp, float humi, float pressure)
  {
    m_temperature = temp;
    m_pressure = pressure;
    m_humidity = humi;

    m_future = temp * humi - pressure;
    display ();
  }
private:
  void display (void)
  {
    cout << "ForcastDisplay::" << __FUNCTION__ << " Current temp=" <<
      m_temperature << " pressure=" << m_pressure << " humidity=" <<
      m_humidity << " // future=" << m_future << endl;
  }
  float m_temperature;
  float m_pressure;
  float m_humidity;
  float m_future;
};

class Subject
{
public:
  virtual void registerObserver (Observer * O) = 0;
  virtual void removeObserver (Observer * O) = 0;
  virtual void notifyObservers (void) = 0;
};

class WeatherData:public Subject
{
public:
  WeatherData (void)
  {
    m_temperature = 0;
    m_humidity = 0;
    m_pressure = 0;
  }

  void setMeasurements (void)
  {
    m_temperature = getTemperature ();
    m_humidity = getHumidity ();
    m_pressure = getPressure ();

    measurementsChanged ();
  }

  void removeObserver (Observer * O)
  {
    observers.remove (O);
  }
  void registerObserver (Observer * O)
  {
    observers.push_back (O);
  }
private:
  void notifyObservers (void)
  {
    for (std::list < Observer * >::iterator it = observers.begin ();
	 it != observers.end (); ++it)
     {
        auto observerObj = *it;
        observerObj->update(m_temperature,m_humidity,m_pressure);
     }
  }
  void measurementsChanged (void)
  {
    notifyObservers ();
  }
  float getTemperature (void)
  {
    cout << "fake WeatherData temperature =30" << endl;
    return 30;
  }
  float getHumidity (void)
  {
    cout << "fake WeatherData humidity = 0.3" << endl;
    return 0.3;
  }
  float getPressure (void)
  {
    cout << "fake WeatherData pressure = 10" << endl;
    return 10;
  }

  float m_temperature;
  float m_humidity;
  float m_pressure;
  std::list < Observer * >observers;
};

int
main ()
{

  ForcastDisplay *ptrForcastObj = new ForcastDisplay;
  WeatherData *ptrWeatherDataObj = new WeatherData;

  ptrWeatherDataObj->registerObserver (ptrForcastObj);

  ptrWeatherDataObj->setMeasurements ();

  ptrWeatherDataObj->removeObserver (ptrForcastObj);

  delete ptrForcastObj;
  delete ptrWeatherDataObj;

  return 0;
}

输出:
fake WeatherData temperature =30
fake WeatherData humidity = 0.3
fake WeatherData pressure = 10
ForcastDisplay::display Current temp=30 pressure=10 humidity=0.3 // future=-1

2.3 设计方案1:数据模型改进方案

2.3.1 设计思路

void update (float temp, float humi, float pressure);
无法将Subject和Observer很好的进行解耦,因此我们使用数据模型进行取代

在这里插入图片描述

2.3.2 实现代码

#include <iostream>
#include <list>
#include <map>
#include <string>

using namespace std;
class Subject;
class Observer;

class DataModel
{
  public:
  std::map<string,float> dataKeyValpair;
};



class DisplayElement
{
public:
  virtual void display (void) = 0;
};

class Observer
{
public:
  virtual void update (Subject& S, DataModel& dataObj) = 0;
};


class ForcastDisplay:public DisplayElement, public Observer
{
public:
  ForcastDisplay (void)
  {
    m_temperature = 0;
    m_pressure = 0;
    m_humidity = 0;
    m_future = 0;
  }


  void update (Subject& S, DataModel& dataObj)
  {
    m_temperature = dataObj.dataKeyValpair["temp"];
    m_pressure = dataObj.dataKeyValpair["pres"];
    m_humidity = dataObj.dataKeyValpair["humi"];
    
    m_future = m_temperature * m_humidity - m_pressure;
    display ();
  }
private:
  void display (void)
  {
    cout << "ForcastDisplay::" << __FUNCTION__ << " Current temp=" <<
      m_temperature << " pressure=" << m_pressure << " humidity=" <<
      m_humidity << " // future=" << m_future << endl;
  }
  float m_temperature;
  float m_pressure;
  float m_humidity;
  float m_future;
};

class Subject
{
public:
  virtual void registerObserver (Observer * O) = 0;
  virtual void removeObserver (Observer * O) = 0;
  virtual void notifyObservers (void) = 0;
};

class WeatherData:public Subject
{
public:
  WeatherData (void)
  {
    m_temperature = 0;
    m_humidity = 0;
    m_pressure = 0;
  }

  void setMeasurements (void)
  {
    m_temperature = getTemperature ();
    m_humidity = getHumidity ();
    m_pressure = getPressure ();

    measurementsChanged ();
  }

  void removeObserver (Observer * O)
  {
    observers.remove (O);
  }
  void registerObserver (Observer * O)
  {
    observers.push_back (O);
  }
private:
  void notifyObservers (void)
  {
    DataModel data;

    data.dataKeyValpair["temp"] = m_temperature;
    data.dataKeyValpair["pres"] = m_pressure;
    data.dataKeyValpair["humi"] = m_humidity;
    
    for (std::list < Observer * >::iterator it = observers.begin ();
	 it != observers.end (); ++it)
     {
        auto observerObj = *it;
        observerObj->update(*this, data);
     }
  }
  void measurementsChanged (void)
  {
    notifyObservers ();
  }
  float getTemperature (void)
  {
    cout << "fake WeatherData temperature =30" << endl;
    return 30;
  }
  float getHumidity (void)
  {
    cout << "fake WeatherData humidity = 0.3" << endl;
    return 0.3;
  }
  float getPressure (void)
  {
    cout << "fake WeatherData pressure = 10" << endl;
    return 10;
  }

  float m_temperature;
  float m_humidity;
  float m_pressure;
  std::list < Observer * >observers;
};

int
main ()
{

  ForcastDisplay *ptrForcastObj = new ForcastDisplay;
  WeatherData *ptrWeatherDataObj = new WeatherData;

  ptrWeatherDataObj->registerObserver (ptrForcastObj);

  ptrWeatherDataObj->setMeasurements ();

  ptrWeatherDataObj->removeObserver (ptrForcastObj);

  delete ptrForcastObj;
  delete ptrWeatherDataObj;

  return 0;
}

2.4 应用场景

Observer和Subject 的实际应用可以考虑如下几种

  • 可以不在同一台主机上,
  • 在同一台主机的不同进程中
  • 同一进程的不同线程中

一个好的框架应该可以兼容以上三种情形,待扩展

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值