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

    情景:

    气象站会实时发布气象数据,要求创建布告板,并把气象站发布的数据显示出来。

    布告板会有很多,随时回添加一个或删除一个,而每个布告板显示的格式也不尽相同。

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

    被观察对象称作主题(Subject),依赖主题的对象称作观察者(Observer)。

    当两个对象之间松耦合,它们依然可以交互,但是不太清除彼此之间的细节。

    观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

    类图: 

    设计原则:为了交互对象之间的松耦合设计而努力。

    气象站的实现:

    主题接口:

    public interface Subject {
        public void registerObserver(Observer o);
        public void removeObserver(Observer o);
        public void notifyObservers();
    }

    观察者接口:

    public interface Observer {
        public void update(float temp, float humidity, float pressure);
    }

    主题(气象站)实现:

    import java.util.ArrayList;
    
    public class WeatherData implements Subject {
        
        private ArrayList<Observer> observers;
        private float temperature;
        private float humidity;
        private float pressure;
        
        public WeatherData() {
            observers = new ArrayList<>();
        }
    
        @Override
        public void registerObserver(Observer o) {
            observers.add(o);
        }
    
        @Override
        public void removeObserver(Observer o) {
            int i = observers.indexOf(o);
            if (i >= 0) {
                observers.remove(i);
            }
        }
    
        @Override
        public void notifyObservers() {
            for (int i = 0; i < observers.size(); i++) {
                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();
        }
        
    }

    观察者(布告板)实现:

    public interface DisplayElement {
        public void display();
    }
    ……………………
    
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
        
        private float temperature;
        private float humidity;
        private Subject weatherData;
        
        public CurrentConditionsDisplay(Subject weatherData) {
            this.weatherData = weatherData;
            weatherData.registerObserver(this);
        }
    
        @Override
        public void display() {
            System.out.println("Current conditions: " + temperature + 
                    "F degrees and " + humidity + "% humdity.");
        }
    
        @Override
        public void update(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            display();
        }
    
    }
    ……………………
    public class ChineseDisplay implements Observer, DisplayElement {
        private float temperature;
        private float humidity;
        private float pressure;
        private Subject weatherData;
        
        public ChineseDisplay(Subject data) {
            this.weatherData = data;
            weatherData.registerObserver(this);
        }
    
        @Override
        public void display() {
            System.out.println("天气状况: 气温:" + temperature + 
                    "F, 湿度:" + humidity + "%, 气压:" + pressure + " Pa。");
        }
    
        @Override
        public void update(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            display();
        }
    }

    测试类:

    public class WeatherStation {
        
        public static void main(String[] args) {
            WeatherData weatherData = new WeatherData();
            CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
            StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
            ChineseDisplay chineseDisplay = new ChineseDisplay(weatherData);
            
            weatherData.setMeasurements(80, 65, 30.4f);
            System.out.println();
            weatherData.setMeasurements(82, 70, 29.2f);
            System.out.println();
            weatherData.setMeasurements(78, 90, 29.2f);
        }
        
    }

    输出:

    Current conditions: 80.0F degrees and 65.0% humdity.
    天气状况: 气温:80.0F, 湿度:65.0%, 气压:30.4 Pa。
    
    Current conditions: 82.0F degrees and 70.0% humdity.
    天气状况: 气温:82.0F, 湿度:70.0%, 气压:29.2 Pa。

    Java中自带Observable类和Observer类,可以通过继承这两个类来实现观察者模式

    主题:

    import java.util.Observable;
    
    public class WeatherData extends Observable {
        private float temperature;
        private float humidity;
        private float pressure;
        
        public WeatherData() {}
        
        public void measurementsChanged() {
            setChanged();
            notifyObservers();
        }
        
        public void setMeasurements(float temperature, float humidity, float pressure) {
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }
        
        public float getTemperature() {
            return temperature;
        }
    
        public float getHumidity() {
            return humidity;
        }
    
        public float getPressure() {
            return pressure;
        }
        
    }

    观察Observable源码setChanged方法

    protected synchronized void setChanged() {
            changed = true;
    }

    有时候改变需要自己定义,比如温度改变0.5℃以上才算改变等……

    notifyObservers方法的实现:

    public void notifyObservers(Object arg) {
            Object[] arrLocal;
    
            synchronized (this) {
                if (!changed)
                    return;
                arrLocal = obs.toArray();
                clearChanged();
            }
    
            for (int i = arrLocal.length-1; i>=0; i--)
                ((Observer)arrLocal[i]).update(this, arg);
        }

    布告板的实现:

    import java.util.Observable;
    import java.util.Observer;
    
    public class CurrentConditionsDisplay implements Observer, DisplayElement {
        
        private Observable observable;
        private float temperature;
        private float humidity;
        
        public CurrentConditionsDisplay(Observable observable) {
            this.observable = observable;
            observable.addObserver(this);
        }
    
        @Override
        public void display() {
            System.out.println("Current conditions: " + temperature + 
                    "F degrees and " + humidity + "% humdity.");
        }
    
        @Override
        public void update(Observable obs, Object arg) {
            if (obs instanceof WeatherData) {
                WeatherData weatherData = (WeatherData) obs;
                this.temperature = weatherData.getTemperature();
                this.humidity = weatherData.getHumidity();
                display();
            }
        }
    
    }

    这里的update()方法和上面的实现不同,上面的实现是push的方法,也就是主题把数据直接送到观察者那里。而这里的是观察者通过getter()方法来自己pull所需要的数据。

    但是java.util.Observable和java.util.Observer都是类而不是接口,所以要继承它们就无法继承别的类了。

    JDK中的观察者模式

    Swing中的JButton就用到了观察者模式。

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    
    public class SwingObserverExample {
        JFrame frame;
        
        public static void main(String[] args) {
            SwingObserverExample example = new SwingObserverExample();
            example.go();
        }
        
        public void go() {
            frame = new JFrame();
            
            JButton button = new JButton("Should I do it?");
            button.addActionListener(new AngelListener());
            button.addActionListener(new DevilListener());
            frame.getContentPane().add(BorderLayout.CENTER, button);
            frame.setSize(200, 200);
            frame.setVisible(true);
        }
        // 一个ActionListener就是一个观察者
        class AngelListener implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Dont do it, you might regret it!");
            }
        }
        
        
        class DevilListener implements ActionListener {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Come on, do it!");
            }
        }
    }
  • 相关阅读:
    在String中添加移动构造函数和移动赋值运算符
    自定义String类,并且实现在STL容器中添加自定义的类型
    allocator例子
    Messages的例子
    java unicode转中文
    Oracle Unicode转中文(解码)
    dom4j解析XML
    如何下载HLS视频到本地(m3u8)
    background-position
    XMPP协议实现即时通讯底层书写 (二)-- IOS XMPPFramework Demo+分析
  • 原文地址:https://www.cnblogs.com/wenruo/p/6511705.html
Copyright © 2011-2022 走看看