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


    前言

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

    设计模式

    观察者模式

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

    要点
    • 观察者模式定义了对象之间一对多的关系
    • 主题(可观察者)用一个共同的接口来更新观察者
    • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
    • 使用次模式时,你可以从被观察者处推或拉数据(推的方式被认为是更正确的)
    • 有多个观察者时,不可以依赖特定的通知次序
    • 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<Observer> observerList;
        private float temp;
        private float humidity;
        private float pressure;
    
        public WeatherData(List<Observer> 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 Observer, DisplayElement {
    
        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("======= 
    name:%s 
    temp:%S 
    humidity:%s 
    pressure:%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 Observer, DisplayElement {
    
        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("======= 
    name:%s 
    temp:%S 
    humidity:%s 
    pressure:%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(12, 88, 981);
            System.out.println("----------------分割线------------------");
            data.setMeasurements(28, 68, 871);
            System.out.println("----------------分割线------------------");
            data.setMeasurements(38, 48, 681);
        }
    
    运作结果

    总结

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

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

  • 相关阅读:
    bootstrap 的页码显示问题-------------德州
    大神的---解决tomcat内存溢出问题----tomcat报错:This is very likely to create a memory leak问题解决
    如何设置tomcat,直接通过IP 访问
    如何把MyEclipse中的web项目导入到Eclipse中运行
    易捷框架之EChart 的使用
    打包jar文件并自动运行
    『PLSQL』在oracle表中怎样创建自增长字段?
    解决MySql 数据库 提示:1045 access denied for user 'root'@'localhost' using password yes
    Oracle 与 MySQL 批量添加
    SPR, subpixel rendering
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/15412911.html
Copyright © 2020-2023  润新知