设计模式之观察者模式

目录

 

观察者模式介绍

气象站

最初的气象应用

布告板接口

布告板实现

气象台监控

测试

布告板发展

气象站观察者模式

测试

Java内置实现观察者

WeatherData

Display

CurrentDisplay

Current2Display

Test

输出结果


观察者模式介绍

观察者模式定义了对象之间的一对多的依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。

 

气象站

下面通过气象站的事例来介绍观察者模式

最初的气象应用

最开始气象站有三个感应装置,分别为温度(temperature)、湿度(humidity)和气压(pressure),显示装置也就只有一个布告板(具体如下),气象站的三个感应装置获取到的最新数据后要展示在布告板上,因此我们可能设计如下的实现:

当前天气情况:
气温:25℃
湿度:60
气压:1atm

布告板接口

public interface Display {
    void display (double temperature, double humidity, double pressure);
}

布告板实现

public class CurrentDisplay implements Display {
    public void display (double temperature, double humidity, double pressure) {
        String content = String.format("当前天气情况:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
        System.out.println(content);
    }
}

气象台监控

public class WeatherData {
    private double temperature;
    private double humidity;
    private double pressure;

    public void weatherDataChange(double temperature, double humidity, double pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        updateDisplay();
    }

    private void updateDisplay() {
        new CurrentDisplay().display(temperature, humidity, pressure);
    }
}

测试

public class Test {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        weatherData.weatherDataChange(25D, 72D, 1.1D);
    }
}

输出结果

当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm

布告板发展

随着业务的发展,布告板的展现形式也越来越多种多样,按照上述的形式,我们只能在WeatherData:updateDisplay()下面不断的新增需要通知的布告板,这个看起来没有什么问题,但是细想下每次新增一个布告板,我们都需要在WeatherData:updateDisplay()下面新加一个通知,没移除一个布告板,都需要将WeatherData:updateDisplay()下面对应的进行移除,天气数据和布告板紧紧的耦合在一起,这个并不符合我们的高内聚低耦合的设计原则,因此我们需要再次进行优化,设计为观察者模式。

 

气象站观察者模式

针对上述,布告板我们无需进行改变,我们对WeatherData进行优化,具体如下(这里我们只介绍设计模式,不考虑线程安全):

public class WeatherData {
    private double temperature;
    private double humidity;
    private double pressure;

    private List displays = new ArrayList();

    //注册观察者
    public void addDisplay(Display display) {
        displays.add(display);
    }

    //取消注册
    public void removeDisplay(Display display) {
        int i = displays.indexOf(display);
        if (i >= 0) {
            displays.remove(display);
        }
    }

    public void weatherDataChange(double temperature, double humidity, double pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        updateDisplay();
    }

    private void updateDisplay() {
        for (Display display : displays) {
            display.display(temperature, humidity, pressure);
        }
    }
}

测试

public class Test {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        Display display1 = new CurrentDisplay();
        Display display2 = new Current2Display();
        Display display3 = new CurrentDisplay();
        weatherData.addDisplay(display1);
        weatherData.addDisplay(display2);
        weatherData.addDisplay(display3);
        System.out.println("----------注册三个观察者----------");
        weatherData.weatherDataChange(25D, 72D, 1.1D);
        weatherData.removeDisplay(display3);
        System.out.println("----------取消第三个观察者----------");
        weatherData.weatherDataChange(28D, 77D, 0.9D);
    }
}

输出结果

----------注册三个观察者----------
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况2:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
----------取消第三个观察者----------
当前天气情况:
气温:28.0℃
湿度:77.0
气压:0.9atm
当前天气情况2:
气温:28.0℃
湿度:77.0
气压:0.9atm

这样我们就把布告板从气象台中抽象出来,后续再添加布告板,我们也无需再次修改WeatherData代码。

 

Java内置实现观察者

Java内置中提供了 java.util.Observer接口和java.util.Observable类来实现观察者模式,下面我们就使用Java内置工具来实现上述气象台的观察者模式。

WeatherData

package com.lulei.study.design.patterns.observer;

import lombok.Data;

import java.util.Observable;

/**
 * Created by lulei on 2019/4/16.
 */
@Data
public class WeatherData extends Observable{
    private double temperature;
    private double humidity;
    private double pressure;


    public void weatherDataChange(double temperature, double humidity, double pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        updateDisplay();
    }

    private void updateDisplay() {
        //在调用notifyObservers()之前,先调用setChanged()来指示状态已经改变
        setChanged();
        //这里我们没有传送数据,表示我们采用的做法是拉
        notifyObservers();
    }
}

Display

package com.lulei.study.design.patterns.observer;

/**
 * Created by lulei on 2019/4/16.
 */
public interface Display {
    void display (double temperature, double humidity, double pressure);
}

 

CurrentDisplay

package com.lulei.study.design.patterns.observer;

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

/**
 * Created by lulei on 2019/4/16.
 */
public class CurrentDisplay implements Observer, Display {
    public CurrentDisplay(Observable observable) {
        observable.addObserver(this);
    }

    public void display (double temperature, double humidity, double pressure) {
        String content = String.format("当前天气情况:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
        System.out.println(content);
    }

    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
            display(weatherData.getTemperature(), weatherData.getHumidity(), weatherData.getPressure());
        }
    }
}

Current2Display

package com.lulei.study.design.patterns.observer;

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

/**
 * Created by lulei on 2019/4/16.
 */
public class Current2Display implements Observer, Display {
    public Current2Display(Observable observable) {
        observable.addObserver(this);
    }

    public void display (double temperature, double humidity, double pressure) {
        String content = String.format("当前天气情况2:\n气温:%s℃\n湿度:%s\n气压:%satm", temperature, humidity, pressure);
        System.out.println(content);
    }

    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)observable;
            display(weatherData.getTemperature(), weatherData.getHumidity(), weatherData.getPressure());
        }
    }
}

 

Test

package com.lulei.study.design.patterns.observer;

import java.util.Observer;

/**
 * Created by lulei on 2019/4/16.
 */
public class Test {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        Observer display1 = new CurrentDisplay(weatherData);
        Observer display2 = new Current2Display(weatherData);
        Observer display3 = new CurrentDisplay(weatherData);
        System.out.println("----------注册三个观察者----------");
        weatherData.weatherDataChange(25D, 72D, 1.1D);
        weatherData.deleteObserver(display3);
        System.out.println("----------取消第三个观察者----------");
        weatherData.weatherDataChange(28D, 77D, 0.9D);
    }
}

 

输出结果

----------注册三个观察者----------
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况2:
气温:25.0℃
湿度:72.0
气压:1.1atm
当前天气情况:
气温:25.0℃
湿度:72.0
气压:1.1atm
----------取消第三个观察者----------
当前天气情况2:
气温:28.0℃
湿度:77.0
气压:0.9atm
当前天气情况:
气温:28.0℃
湿度:77.0
气压:0.9atm

 

你可能感兴趣的:(设计模式)