zoukankan      html  css  js  c++  java
  • 设计模式之观察者模式

    题记

    最近开始阅读《Head First设计模式》,所以有必要把自己学到的东西用blog的方式给记录下来,其中,大部分的源码和UML关系图均是来自于该书。在此,本系列的文章只是将学到的知识记录一下,必要的地方会加上个人的理解。

    正文

    在我们的开发过程中,难免会遇到一种情况:存在一个主要对象,其他的对象因为由于数据的关系,必须依赖于这个对象产生的数据,但这个对象的数据总是处于不断的话当中,因此,我们希望这个主要对象的数据一旦发生变化,那么就能够及时的通知到依赖于它的对象。而当我们取消了这种依赖之后,随之而来就是不用再去通知已经取消的对象。其实,这个很像现在的微信公众平台,只要我们关注了某个公众账号后,公众账号就会不断的去推送最新的消息给关注者,而一旦取消关注后,这些推送也就中断了。在设计模式中,我们把这种行为称之为观察者模式(observer pattern)

    对于观察者模式,我们有着这样的书面定义:

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

    观察者有着如下的类图:

    其实这么看的话,也是不清不楚的,这个只是给出了观察者模式的一种通用类图,而至于如何具体的运作,我们也是不甚了解。

    以下就是以天气通知为例,利用具体代码具体说明整个观察者模式。

    其中,Subject为主题对象接口,也就是前面所说的数据变化的源头,其他对象都是它的观察者;Observer为观察者接口;DisplayElement为显示接口,这个由于每个观察者对于显示有着不同的要求,因此独立开辟这样一个接口。WeatherData继承了Subject接口,生成了具体的主题对象,而观察者真正观察到的数据,就是由该具体对象提供。CurrentConditonsDisplayForecastDisplayStatisticsDisplayThirdPartydDisplay这几个则是真正的观察者,当主题对象的数据产生了变化之后,就会主动的一一通知到这些个观察者,然后由它们根据各自的显示需求显示相应的数据(由于显示要求的不同,因此它们也继承显示接口DisplayElement)。

    下面就让我们看看具体的运作代码。

    //观察对象接口
    public interface Subject
    {
        public void registerObserver(Observer o);
        public void removeObserver(Observer o);
        public void notifyObservers(Observer o);
    }
    
    //观察者接口
    public interface Observer
    {
        public void update(float temp, float humidity, float pressure);
    }
    
    //显示接口
    public interface DisplayElement
    {
        public void display();
    }
    

    以上定义各个接口,以及其中需要继承的公用方法,下面就是各个具体对象的实现了。

    public class WeatherData implements Subject
    {
        private ArrayList observers;
        private float temperature;
        private float humidity;
        private float pressure;
    
        public WeatherData()
        {
            observers = new ArrayList();
        }
    
        public void registerObserver(Observer o)
        {
            observers.add(o);
        }
    
        public void removeObserver(Observer o)
        {
            int i = observers.indexof(o);
            if(i >= 0)
            {
                observers.remove(i);
            }
        }
    
        public void notifyObservers(Observer o)
        {
            for(int i = 0; i < observers.size(); i++)
            {
                Observer observer = (Observer)observers.get(i);
                observer.update(temperature, humidity, pressure);
            }
        }
    
        public void measurementsChanged()
        {
            notifyObservers();
        }
    
        public void setMeasurements(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
    
        // WeatherData的其他方法
    }
    

    这个是主题对象的具体实现WeatherData,它继承了Subject接口,其中具体实现了各个方法,其中setMeasurements()方法则是保证了在发生了变化时,第一时间能够将数据的变化通知到各个观察者。接下来,就是等着实现各个观察者了,这里只拿一个观察者CurrentConditonsDisplay作为实现的案例。

    public class CurrentConditonsDisplay implements Observer, DisplayElement
    {
        private float temperature;
        private float humidity;
        private Subject weatherData;
    
        public CurrentConditonsDisplay(Subject weatherData)
        {
            this.weatherData = weatherData;
            weatherData.registerObserver(this);
        }
    
        public void update(float temperature, float humidity, float pressure)
        {
            this.temperature = temperature;
            this.humidity = humidity;
            display();
        }
    
        public void display()
        {
            System.out.println("Current conditions: " + temperature + "F degree and " + humidity + "% humidity");
        }
    }
    

    这段代码似乎没有什么好说道的,唯一的特殊的地方就是在构建方法中,我们调用Subject对象,这在注册观察者时用到了,其实,在后期如果我们加上取消关注的时候,也是会用到这个对象的,所以从方便的角度考虑,我们在构造方法中调用了该对象。

    剩下来就是个简单的测试代码了。

    public class WeatherStation()
    {
        public static void main(String[] args)
        {
            WeatherData weatherData = new WeatherData();
            CurrentConditonsDisplay currentDisplay = new CurrentConditonsDisplay(weatherData);
            StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
            ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
    
            weatherData.setMeasurements(80, 65, 30.4f);
        }
    }
    

    当我们理清了这些调用继承关系之后,豁然发现观察者模式其实挺简单的。而观察者模式的最重要的地方,就是在有一个主动数据源,多个被动读取数据的对象的情况下,让数据的操作变得单一简单,不会出现太多的对象操作数据的情况。

  • 相关阅读:
    案例分享:只因在 update 语句中误用一个双引号,生产数据竟然都变成了 0
    快速了解Service Mesh微服务架构实现服务间gRPC通信
    实战|如何优雅地自定义Prometheus监控指标
    微服务架构中如何快速构建一个数据报告服务?
    k8s微服务接入SkyWalking,三分钟教你怎么玩!
    Python基础-27-面向对象
    Python基础-21-字典
    Jmeter JSON提取器
    Jmeter正则表达式提取器
    访问github,修改host文件
  • 原文地址:https://www.cnblogs.com/panpei/p/3172914.html
Copyright © 2011-2022 走看看