观察者模式.

一、概念

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

  • 观察者模式四个角色:
     1、抽象主题:定义对观察者管理的接口,包括 订阅、取消订阅、通知接口。
     2、具体主题:把所有观察者对象的引用保存在一个聚集(比如ArrayList对象)里,实现抽象主题的接口,并在更新时通知观察者。
     3、抽象观察者:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
     4、具体观察者:实现抽象观察者定义的接口,得到主题通知的数据(pull or push),并实现自己的逻辑。

  • 主题 + 观察者 = 观察者模式,可以用报纸订阅服务来模拟这个模式 —— 报纸是主题,订阅报纸的人是观察者。观察者可以选择是否订阅或者退订主题。

二、Demo实现

1、主题

java.util 包中内置了一个抽象主题 Observable 类(有弊端,下文会提到),可以选择继承 Observable 类作为具体主题角色。

@AllArgsConstructor
@Getter
public class WeatherData extends Observable {
    /**
     * 温度
     */
    private Float temperature;
    /**
     * 湿度
     */
    private Float humidity;
    /**
     * 压力
     */
    private Float pressure;
    
    
    /**
     * 假设改变的时候这个方法会被调用
     */
    public void measurementsChanged() {
        setChanged();
        notifyObservers(); // 这种不带参数的方式,由观察者 pull 自己想要的数据。要什么数据由观察者自己决定。
        //notifyObservers(Object arg) // 这种带参数的方式,由主题 push 数据。传什么数据由主题决定。(推荐)
    }
}

2、观测者

java.util 包中内置了一个抽象观察者 Observer 接口,可以选择实现 Observer 接口的 update 方法作为具体观察者角色,做更新的操作。

湿度观察者:

public class HumidityObserver implements Observer {

    public HumidityObserver(Observable observable) {
        // 订阅主题
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            Float humidity = weatherData.getHumidity();
            System.out.println("我是一个湿度观察者,我现在的湿度是:" + humidity);
        }
    }
}

压力观察者:

public class PressureObserver implements Observer {
    
    public PressureObserver(Observable observable) {
        // 订阅主题
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            Float pressure = weatherData.getPressure();
            System.out.println("我是一个压力观察者,我现在的压力是:" + pressure);
        }
    }
}

温度观察者:

public class TemperatureObserver implements Observer {
    
    public TemperatureObserver(Observable observable) {
        // 订阅主题
        observable.addObserver(this);
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            Float temperature = weatherData.getTemperature();
            System.out.println("我是一个温度观察者,我现在的温度是:" + temperature);
        }
    }
}

3、测试

public static void main(String[] args) {
    // 具体主题实现
    WeatherData weatherData = new WeatherData(24.0F,152F,100F);
    // 观察者订阅
    new HumidityObserver(weatherData);
    new PressureObserver(weatherData);
    new TemperatureObserver(weatherData);
    // 事件发生
    weatherData.measurementsChanged();
}

4、Observable 的弊端

  • Observable 是一个“类”,而不是一个接口。所以你只能选择继承它,实现自己的具体主题,这大大限制了 Observable 的复用潜力,毕竟 Java 不支持多重继承。
  • Observable 的 setChanged 方法是 protected 属性的,因此你除了选择继承 Observable 类,甚至无法使用组合来使用它。
  • 如果有必要的话可以实现自己的 Observable 接口,不要害怕,这并不难。(链接代码中有示例)

avatar
tips:"抽象"主题 和 具体主题标反了。汗~

演示源代码:https://github.com/JMCuixy/design-patterns

三、总结

  • 设计原则:观察者模式是为了交互对象之间的松耦合设计而努力。
  • 应用场景:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
  • 优点:主题是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样比起让许多对象控制通一份数据类,可以得到更加干净的OO设计。
  • 缺点:在应用观察者模式时需要考虑一下开发效率和运行效率的问题,因为在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,我们一般会采用异步实现。而且必须保证投递是以自恰的方式进行的。
posted @ 2018-11-10 21:03  JMCui  阅读(574)  评论(0编辑  收藏  举报