Implementación observable de jdk en modo observador java

Concepto:

Modo observador:

El patrón de observador define las dependencias de uno a muchos entre los objetos, de modo que cuando un objeto cambia de estado, todos sus dependientes serán notificados y actualizados automáticamente. -> Comunicar el estado entre una serie de objetos de una manera poco acoplada

El artículo anterior implementó el modo de observador con código personalizado. Este artículo explica principalmente la implementación de jdk y explora las ventajas y desventajas de este modo en jdk:

Escenario comercial:

Hay una estación de observación meteorológica que puede detectar datos meteorológicos específicos, como temperatura, humedad y presión barométrica. Ahora hay tres paneles de visualización específicos. Cuando se actualizan los datos meteorológicos, el observador puede notificar a los tres paneles de visualización para actualizar los datos en tiempo real. Este escenario empresarial simplemente se ajusta a nuestro modelo de observador, la estación de observación meteorológica es el sujeto y el panel de visualización es el observador. Esta vez, el negocio se implementa a través del observable construido en jdk:
Primero, se presentan las clases e interfaces construidas en jdk:

  1. public interface Observer {…}
    La interfaz que el observador necesita implementar, solo hay un método de actualización, que se llama cuando se recibe la notificación del tema
/*
package java.util;
public interface Observer {
    void update(Observable o, Object arg);
}

  1. public class Observable {…}
    La clase de la que el tema necesita heredar, y su implementación no es difícil.
package java.util;
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;//使用线程安全的Vector类来存储观察者
    public Observable() {
        obs = new Vector<>();
    }

	//添加观察者
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    
   //删除观察者
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

	//通知观察者,不带参数
    public void notifyObservers() {
        notifyObservers(null);
    }

	//通知观察者,带参数
    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
	//循环遍历,并且在changed为true的时候才会通知
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
 	//删除观察者
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
	//改变状态,为true才会通知观察者
    protected synchronized void setChanged() {
        changed = true;
    }
  //清除状态
    protected synchronized void clearChanged() {
        changed = false;
    }
	//返回状态
    public synchronized boolean hasChanged() {
        return changed;
    }
    //获取观察者的数量
    public synchronized int countObservers() {
        return obs.size();
    }
}

Se puede ver que la implementación de jdk no es difícil. El tema solo necesita heredar la clase Observable y llamar al método setChanged (), y luego al método notifyObservers () donde se necesita notificación, y el observador implementa la interfaz Observer y se implementa a sí mismo. El método de actualización puede lograr el objetivo.
Veamos el código de implementación de este caso meteorológico:

  • Temas especificos:
package observer.weatherobservable;

import java.util.Observable;

/**
 * 主题,需要继承Observable类
 */
public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {

    }

    public void measurementsChanged(){
        setChanged();
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}
  • La interfaz que el panel de visualización necesita implementar:
package observer.weatherobservable;
/**
 * 显示面板需要实现的接口,这个根据具体的场景设计
 */
public interface DisplayElement {
    public void display();
}
  • Panel de visualización de temperatura actual:
package observer.weatherobservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 当前温度面板
 */
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    private Observable observable;
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.println("当前状态是 " + temperature
                + "华氏度 和 " + humidity + "% 湿度");
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }
}
  • Panel de visualización de pronósticos:
package observer.weatherobservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 预报的显示面板
 */
public class ForecastDisplay implements Observer, DisplayElement {
    private float currentPressure = 29.92f;
    private float lastPressure;

    public ForecastDisplay(Observable observable) {
        observable.addObserver(this);
    }

    @Override
    public void display() {
        System.out.print("预报: ");
        if (currentPressure > lastPressure) {
            System.out.println("温度正在上升!");
        } else if (currentPressure == lastPressure) {
            System.out.println("气温没有变化");
        } else if (currentPressure < lastPressure) {
            System.out.println("天气变冷了");
        }
    }
    
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            lastPressure = currentPressure;
            currentPressure = weatherData.getPressure();
            display();
        }
    }
}
  • Panel de visualización del índice de calor:
package observer.weatherobservable;

import java.util.Observable;
import java.util.Observer;

/**
 * heat index的显示面板
 */
public class HeatIndexDisplay implements Observer, DisplayElement {
    float heatIndex = 0.0f;

    public HeatIndexDisplay(Observable observable) {
        observable.addObserver(this);
    }

    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
            float t = weatherData.getTemperature();
            float rh = weatherData.getHumidity();
            heatIndex = (float)
                    (
                            (16.923 + (0.185212 * t)) +
                                    (5.37941 * rh) -
                                    (0.100254 * t * rh) +
                                    (0.00941695 * (t * t)) +
                                    (0.00728898 * (rh * rh)) +
                                    (0.000345372 * (t * t * rh)) -
                                    (0.000814971 * (t * rh * rh)) +
                                    (0.0000102102 * (t * t * rh * rh)) -
                                    (0.000038646 * (t * t * t)) +
                                    (0.0000291583 * (rh * rh * rh)) +
                                    (0.00000142721 * (t * t * t * rh)) +
                                    (0.000000197483 * (t * rh * rh * rh)) -
                                    (0.0000000218429 * (t * t * t * rh * rh)) +
                                    (0.000000000843296 * (t * t * rh * rh * rh)) -
                                    (0.0000000000481975 * (t * t * t * rh * rh * rh)));
            display();
        }
    }

    public void display() {
        System.out.println("Heat index 是 " + heatIndex);
    }
}
  • Panel de visualización de temperatura:
package observer.weatherobservable;

import java.util.Observable;
import java.util.Observer;

/**
 * 溫度顯示面板
 */
public class StatisticsDisplay implements Observer, DisplayElement {
    private float maxTemp = 0.0f;
    private float minTemp = 200;
    private float tempSum= 0.0f;
    private int numReadings;

    public StatisticsDisplay(Observable observable) {
        observable.addObserver(this);
    }

    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
            float temp = weatherData.getTemperature();
            tempSum += temp;
            numReadings++;

            if (temp > maxTemp) {
                maxTemp = temp;
            }

            if (temp < minTemp) {
                minTemp = temp;
            }

            display();
        }
    }

    public void display() {
        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
                + "/" + maxTemp + "/" + minTemp);
    }
}
  • Código de prueba:
package observer.weatherobservable;

/**
 * 測試
 */
public class WeatherStationHeatIndex {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData);//当前状态是
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);//Avg/Max/Min
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);//预报:
        HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);//Heat index 是

        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

Resultados de la prueba:
Heat index 是 82.95535
预报: 温度正在上升!
Avg/Max/Min temperature = 80.0/80.0/80.0
当前状态是 80.0华氏度 和 65.0% 湿度
Heat index 是 86.90124
预报: 天气变冷了
Avg/Max/Min temperature = 81.0/82.0/80.0
当前状态是 82.0华氏度 和 70.0% 湿度
Heat index 是 83.64967
预报: 气温没有变化
Avg/Max/Min temperature = 80.0/82.0/78.0
当前状态是 78.0华氏度 和 90.0% 湿度

Por supuesto, esto también puede obtener los resultados del modo de observador que deseamos. Cuando la estación meteorológica detecta cambios en los datos meteorológicos, cada panel de visualización puede mostrar los últimos datos meteorológicos a tiempo.
Sin embargo, encontrará los resultados obtenidos y agregamos El orden de los observadores es inconsistente, ¿por qué?
Mire detenidamente el código fuente de Observer: encontrará que atraviesa la colección hacia atrás, jaja, ¿no sabe por qué?
Inserte la descripción de la imagen aquí
Finalmente, mencione el booleano privado cambiado = falso; el papel de esta variable, es permitirnos notificar cuando necesitamos notificar, aunque el tema ha cambiado, mientras esta variable no haya cambiado, no podemos notificar.

Finalmente, resuma las ventajas y desventajas de la clase java.util.Observable y la interfaz de Observador para lograr el patrón de observador:

  1. Observer es una clase, por lo que debe diseñar una clase para heredar esta clase, y la clase java es una herencia única, lo que limita nuestras ideas de diseño;
  2. Observer's setchanged () está protegido, es decir, debe cambiarlo en su subclase, lo que viola los principios de diseño: más uso de composición, menos uso de herencia;
  3. El método para notificar a los observadores se ejecuta secuencialmente en el hilo principal: qué observador unirse está bloqueado y los observadores posteriores tienen que esperar, lo que es muy hostil;
  4. Además de las deficiencias anteriores, el resto son todas ventajas.
    En vista de las deficiencias anteriores, si queremos resolver los problemas anteriores, finalmente personalizamos la implementación de un patrón de observación.Si se pueden aceptar las deficiencias anteriores, podemos reutilizar el código jdk para lograr
Publicado 39 artículos originales · ganado elogios 1 · vistas 4620

Supongo que te gusta

Origin blog.csdn.net/thetimelyrain/article/details/97055841
Recomendado
Clasificación