设计模式详解——观察者模式

云中志

共 4261字,需浏览 9分钟

 · 2021-10-16

前言

今天我们来看下一个可以有效实现松耦合的设计模式——观察者模式,这个设计模式我之前也仅仅停留在听说过的层面,关于它的具体实现更是一知半解,所以今天我们就来简单剖析下这个设计模式。

设计模式

观察者模式

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

要点
  • 观察者模式定义了对象之间一对多的关系
  • 主题(可观察者)用一个共同的接口来更新观察者
  • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
  • 使用次模式时,你可以从被观察者处推或拉数据(推的方式被认为是更正确的)
  • 有多个观察者时,不可以依赖特定的通知次序
  • java中有多种观察者模式的实现,包括了通用的java.util.Observable,不过需要注意Observable实现上所带来的问题,有必要的话,可以实现自己的Observable
  • Swing大量使用观察者模式,许多GUI框架也是如此
  • 观察者模式还广泛被应用在许多地方,比如:JavaBeansRMI
示例

下面我们以一个具体实例,来展示下观察者模式的具体实现。这里我们就直接以《Head First设计模式》中的气象站为例,其中天气信息就表示被观察者,天气布告板就表示订阅者和观察者,当天气发生变化(被观察者)时,会通过notifyObserver通知所有观察者,并调用他们的控制方法处理数据。下面我们就来看下具体实现过程

被观察者接口接口

这里有三个方法,第一个是注册观察者,第二个是移除观察者,第三个是通知观察者

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObserver();
}
观察者接口

观察者接口就一个方法,主要用于更新从被观察者中获取到的数据,该方法会在被注册者的notifyObserver方法中被调用

public interface Observer {
    void update(float temp, float humidity, float pressure);
}
控制层接口

这个接口主要是用于处理被观察者更新的数据,核心方法就一个。

public interface DisplayElement {
    void display();
}
被观察者实现

这里是被观察者的具体实现,被观察者也可以叫数据源,当数据发生变化时,由它负责告知观察者。

public class WeatherData implements Subject {
    private List observerList;
    private float temp;
    private float humidity;
    private float pressure;

    public WeatherData(List observerList) {
        this.observerList = observerList;
    }

    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observerList.remove(observer);
    }

    @Override
    public void notifyObserver() {
        observerList.forEach(observer -> observer.update(temp, humidity, pressure));
    }

    public void measurementChanged() {
        notifyObserver();
    }

    public void setMeasurements(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementChanged();
    }
}
观察者实现

观察者我们实现了两个,主要是便于后面测试。这两个实现处理名字不一样,其他代码都是一样的。

public class RemoteDisplay implements ObserverDisplayElement {

    private Subject subject;
    private float temp;
    private float humidity;
    private float pressure;

    public RemoteDisplay(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println(String.format("======= \nname:%s \ntemp:%S \nhumidity:%s \npressure:%s""remote", temp, humidity, pressure));
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
}

public class CurrentWeatherDataDisplay implements ObserverDisplayElement {

    private Subject subject;
    private float temp;
    private float humidity;
    private float pressure;

    public CurrentWeatherDataDisplay(Subject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void display() {
        System.out.println(String.format("======= \nname:%s \ntemp:%S \nhumidity:%s \npressure:%s""urrentWeatherData", temp, humidity, pressure));
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temp = temp;
        this.humidity = humidity;
        this.pressure = pressure;
        display();
    }
}
测试代码

从上面观察者的实现类中,我们可以看出来,在实例化观察者的时候,其实已经完成了注册操作,所以这里我们不再需要手动注册观察者,后面再被观察者(数据)发生变化时,观察者会实时被通知。

@Test
    public void testWeatherDisplay() {
        WeatherData data = new WeatherData(new ArrayList<>());
        new CurrentWeatherDataDisplay(data);
        new RemoteDisplay(data);
        data.setMeasurements(1288981);
        System.out.println("----------------分割线------------------");
        data.setMeasurements(2868871);
        System.out.println("----------------分割线------------------");
        data.setMeasurements(3848681);
    }
运作结果

总结

从实例的运行结果来看,观察者模式特别适用于那些一对多的应用场景,比如数据推送同步,当你的平台数量发生变化时,数据发送方只需要将相关平台注册或删除,即可完成观察者的变更,可以实现代码之间的松耦合,提升代码的扩展性。

与这个设计模式类似的设计模式是订阅者模式,这两种设计模式,在我之前的认知中,我觉得他们应该是一样的,但是今天查阅了相关资料之后,发现它们并不完全一样,这里我们先大概对订阅者模式有一个基本的认知,后面等我们分享完订阅者模式之后,我们再来比较这两个的区别。

- END -


浏览 23
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报