Introducción a los patrones de diseño (2) Patrón de observador

Comenzando con los patrones de diseño

Todo el contenido de esta serie tiene como referencia el "Patrón de diseño HeadFirst". Debido a que el código del libro está escrito en lenguaje Java, el blogger lo reescribe en lenguaje C++.
Se explica aquí en forma de narración. Si hay algún error, su orientación es bienvenida.
Patrones de diseño: los patrones no son códigos, sino soluciones generales a problemas de diseño, que se consideran experiencias probadas de diseño OO. Los patrones de diseño nos dicen cómo organizar clases y objetos para resolver un problema determinado.
Si desea utilizar patrones de diseño al generar un helloworld, es posible que realmente haya un problema.

texto

Hacer una pregunta

Ahora tenemos a mano una aplicación de detección del tiempo. La estación meteorológica recibe datos del dispositivo sensor de humedad , del dispositivo sensor de temperatura y del dispositivo sensor de presión del aire , y luego tenemos un objeto WeatherData, que es responsable de rastrear los datos de la estación meteorológica y actualizar el tablón de anuncios (que muestra el tiempo actual). condiciones al usuario).
Imagen del patrón de diseño HeadFirst
Si tuviéramos que hacernos cargo de este proyecto, nuestro trabajo sería crear una aplicación que utilice el objeto WeatherData para obtener datos y actualizar tres tableros de anuncios: condiciones actuales, estadísticas meteorológicas y pronósticos meteorológicos . Los tres tablones de anuncios son los que se muestran a continuación:
Insertar descripción de la imagen aquí

El código fuente de la clase WeatherData existente es el siguiente:

class WeatherData {
    
    
    float getTemperature();  //返回温度
    float getHumidity();     //返回湿度
    float getPressure();     //返回气压
    void measurementsChanged()
    {
    
    
        /*一旦气象测量更新,此方法会被调用*/
        //我们的代码加在这里
    }
};

Nuestro trabajo es implementar medicionesChanged() para que actualice el tablero de visualización con las condiciones actuales, estadísticas meteorológicas y pronósticos meteorológicos .

Lo que sabemos hasta ahora: La clase WeatherData tiene tres métodos que pueden obtener tres valores de medición; cuando lleguen nuevos datos, se llamará al método medicionesChanged() (no nos importa cómo se llame este método, solo nos importa que sea llamado) ); Necesitamos implementar tres tableros de anuncios que utilicen datos meteorológicos. Una vez que WeatherData tenga nuevas mediciones, estos boletines deben actualizarse inmediatamente.

Una implementación de medidasChanged() que podríamos pensar es la siguiente:

class WeatherData {
    
    
	// 实例变量声明
    void measurementsChanged()
    {
    
    
    	// 获取最新的测量值
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        
        // 调用每个布告板更新显示
        currtenConditionsDisplay.update(temp, humidity, pressure);  // 目前状况布告板更新
        statisticsDisplay.update(temp, humidity, pressure);  // 气象统计布告板更新
        forecastDisplay.update(temp, humidity, pressure);   // 天气预报布告板更新
    }
};

Pero esto entra en conflicto con algunos principios de diseño de software. La llamada a cada función de visualización de actualización del tablero de anuncios en el código anterior se basa en una programación de implementación específica, lo que hará que modifiquemos el programa al agregar o eliminar tableros de anuncios en el futuro; las tres interfaces se actualizan y los parámetros se pasan También son iguales, por lo que parece más una interfaz unificada.

Entonces, ¿cómo solucionamos este problema? El patrón de observador puede ayudarnos muy bien a resolver este problema.

Patrón de observador

Un ejemplo muy sencillo es la suscripción a una revista .
Supongamos que nos suscribimos a una revista, y cada vez que la revista se actualiza, nos enviará una copia. Este es el patrón del observador. La revista equivale al "tema" y nosotros somos equivalentes al "observador". Cuando el tema cambia, se notifica al "observador". Una cosa a tener en cuenta aquí es que los temas se utilizan para agregar o eliminar observadores. Cuando queramos suscribirnos a una revista, el editor de la revista nos agregará a su lista de suscripción. Cuando no queramos suscribirnos a la revista, el editor de la revista nos eliminará de la lista de suscripción.

Patrón de observador: El patrón de observador define una dependencia de uno a muchos entre objetos ("un sujeto" versus "muchos observadores"), de modo que cuando un objeto cambia de estado, todas sus dependencias (debido al sujeto La persona que realmente posee los datos (el observador es la dependencia del sujeto) serán notificados y actualizados automáticamente.

El código de implementación es el siguiente:

#include<iostream>
#include<vector>

using namespace std;

class Observer {
    
      // 观察者
public:
    virtual void update(float temp, float humidity, float pressure) = 0;
};

class Subject {
    
      // 抽象主题
    virtual void registerObserver(Observer *o)=0;
    virtual void removeObserver(Observer *o)=0;
    virtual void notifyObserver()=0;
};

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

class WeatherData : public Subject  // 具象主题
{
    
    
private:
    vector<Observer*> observers;
    float temperature;
    float humidity;
    float pressure;
public:
    void registerObserver(Observer *o)  // 注册观察者
    {
    
    
        observers.push_back(o);
    }

    void removeObserver(Observer *o)   // 取消观察者
    {
    
    
        auto it = std::find(observers.begin(), observers.end(), o);

        if (it != observers.end())
        {
    
    
            int index = std::distance(observers.begin(), it);
            cout << "索引是:" << index << endl;;
            observers.erase(observers.begin() + index);
            cout << "成功删除元素" << endl;
        }
        else
        {
    
    
            cout << "未找到元素" << endl;
        }
    }

    void notifyObserver()  // 通知观察者
    {
    
    
        for (int i = 0; i < observers.size(); i++)
        {
    
    
            Observer *observer = observers[i];
            observer->update(temperature, humidity, pressure);
        }
    }
    void measurementsChanged()
    {
    
    
        notifyObserver();  // 通知观察者
    }

    void setMeasurements(float temperature, float humidity, float pressure)
    {
    
    
        this->temperature = temperature; 
        this->humidity = humidity;
        this->pressure = pressure;
        measurementsChanged();
    }
};

class StatisticsDisplay : public Observer, public DisplayElement  // 观察者
{
    
    
private:
    float temperature;
    float humidity;
    WeatherData *weatherData;
public:
    StatisticsDisplay(WeatherData *weather)
    {
    
    
        weatherData = weather;
        weatherData->registerObserver(this);  //主题注册观察者
    }

    void remove()
    {
    
    
        weatherData->removeObserver(this); // 主题取消观察者
    }

    void update(float temperature, float humidity, float pressure)
    {
    
    
        this->temperature = temperature;
        this->humidity = humidity;
        display();
    }
    void display()
    {
    
    
        cout << "statisticsDisplay: " << temperature << "F degress and " << humidity << "% humidity" << endl;
    }
};

class ForecastDisplay : public Observer, public DisplayElement  // 观察者
{
    
    
private:
    float temperature;
    float humidity;
    WeatherData *weatherData;
public:
    ForecastDisplay(WeatherData *weather)
    {
    
    
        weatherData = weather;
        weatherData->registerObserver(this);  //主题注册观察者
    }

    void remove()
    {
    
    
        weatherData->removeObserver(this); // 主题取消观察者
    }

    void update(float temperature, float humidity, float pressure)
    {
    
    
        this->temperature = temperature;
        this->humidity = humidity;
        display();
    }
    void display()
    {
    
    
        cout << "ForecastDisplay: " << temperature << "F degress and " << humidity << "% humidity" << endl;
    }
};

class CurrentConditionsDisplay : public Observer, public DisplayElement  // 观察者
{
    
    
private:
    float temperature;
    float humidity;
    WeatherData *weatherData;
public:
    CurrentConditionsDisplay(WeatherData *weather)
    {
    
    
        weatherData = weather;
        weatherData->registerObserver(this);  //主题注册观察者
    }

    void remove()
    {
    
    
        weatherData->removeObserver(this); // 主题取消观察者
    }

    void update(float temperature, float humidity, float pressure)
    {
    
    
        this->temperature = temperature;
        this->humidity = humidity;
        display();
    }
    void display()
    {
    
    
        cout << "CurrentConditionsDisplay: " << temperature << "F degress and " << humidity << "% humidity" << endl;
    }
};
int main()
{
    
    
    WeatherData *weatherData = new WeatherData; // 定义一个主题对象即可
    CurrentConditionsDisplay currentDisplay(weatherData);  // 第一个观察者
    StatisticsDisplay statisDisplay(weatherData);   // 第二个观察者
    ForecastDisplay foreDisplay(weatherData);   // 第三个观察者
    weatherData->setMeasurements(80, 65, 30.4);   // 主题信息发生变更
    
    currentDisplay.remove();   // 该观察者取消对主题的订阅

    weatherData->setMeasurements(40, 25, 15.4);

    foreDisplay.remove();   // 该观察者取消对主题的订阅

    weatherData->setMeasurements(15.5, 26, 34);
    return 0;
}

Lo anterior es todo el código para implementar el patrón de observador usando C++.

Criterios de diseño

  1. Encuentre los aspectos de su programa que cambian y sepárelos de los aspectos que están fijos.
    En el modo de observación, lo que cambia es el estado del sujeto, así como el número y tipo de observadores. Con este patrón, puede cambiar objetos que dependen del estado del tema sin cambiar el tema.
  2. Programación para interfaces, no programación de implementación.
    Tanto los temas como los observadores usan interfaces: los observadores usan la interfaz del tema para registrarse con el tema, y ​​el tema usa la interfaz del observador para notificar a los observadores. Esto permite que los dos funcionen normalmente teniendo la ventaja de un acoplamiento flojo.

El patrón de observador es más importante y se puede ver en muchos marcos y diseños de software, por lo que puede comprender cuidadosamente sus ideas basadas en el código. Durante los pocos meses que estuve trabajando, vi el patrón de observador en el software de la empresa, pero no lo implementé yo mismo, simplemente entendí lo que significaba. Lo implementé yo mismo hoy y mis conocimientos son más profundos.

Supongo que te gusta

Origin blog.csdn.net/qq_41596730/article/details/126231265
Recomendado
Clasificación