设计模式——观察者模式

观察者模式是什么?

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

观察者模式解决什么问题?

天气数据通常来源于卫星或者气象局传感器(通过setWeatherData存储),每当新的天气数据到来时,就需要提醒各个Tv频道进行更新(在WeatherData中调用dataChanged()通知各个Tv)

public class WeatherData {

    private int lowTemperature;
    private String weather;
    private int highTemperature;

    public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
        this.lowTemperature = lowTemperature;
        this.weather = weather;
        this.highTemperature = highTemperature;
        dataChanged();
    }

    public void dataChanged() {
        BeijingTv.update(lowTemperature, weather, highTemperature);
        GuangdongTv.update(lowTemperature, weather, highTemperature);
    }
}

class BeijingTv {
    public static void update(int lowTemperature, String weather, int highTemperature) {
        System.out.println("北京天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
    }
}

class GuangdongTv {
    public static void update(int lowTemperature, String weather, int highTemperature) {
        System.out.println("广东天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
    }
}
  • problem1:当有新频道需要获取天气数据时,需要修改WeatherData的源代码
  • problem2:更新的方法都是类似的

观察者模式实现1——推送数据

被观察者接口

被观察者有注册、取消注册、通知三个方法

interface Subject{
    void registerObserver(Observer o);
    void unregisterObserver(Observer o);
    void notifyObserver();
}

观察者接口

观察者有一个通知方法

interface Observer{
    void update(int lowTemperature, String weather, int highTemperature);
}

被观察者实例

WeatherData实现Subject接口,内部通过ArrayList维护观察者

public class WeatherData implements Subject {

    private int lowTemperature;
    private String weather;
    private int highTemperature;
    private ArrayList<Observer> observers;

    public WeatherData() {
        observers = new ArrayList<>();
    }


    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void unregisterObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i > 0) {
            observers.remove(o);
        }
    }

    @Override
    public void notifyObserver() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.update(lowTemperature, weather, highTemperature);
        }
    }

    public void dataChanged() {
        notifyObserver();
    }

    public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
        this.lowTemperature = lowTemperature;
        this.weather = weather;
        this.highTemperature = highTemperature;
        dataChanged();
    }
}

被观察者实例

各个Tv频道通过实现Observer重写update()方法完成对数据的操作

class BeijingTv implements Observer {

    @Override
    public void update(int lowTemperature, String weather, int highTemperature) {
        System.out.println("北京天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
    }
}

class GuangdongTv implements Observer {

    @Override
    public void update(int lowTemperature, String weather, int highTemperature) {
        System.out.println("广东天气:" + weather + ",最低气温:" + lowTemperature + ",最高气温:" + highTemperature);
    }
}

通知过程

观察者注册:

WeatherData weatherData=new WeatherData();
BeijingTv beijingTv=new BeijingTv();
weatherData.registerObserver(beijingTv);

当数据改变时,会通知各个观察者更新数据:

weatherData.setWeatherData(10,"晴转多云",20);

控制数据推送频率

天气数据每分钟都会发生变化,会造成被观察者不断的向观察者推送数据,更该采取的措施是每个半小时或者当温度变化0.5°时再进行推送更新。

通过添加标志量changed,可以控制数据的更新:

private boolean changed = false;

    public void setChanged(boolean changed) {
        this.changed = changed;
    }

    public void dataChanged() {
        if (changed) {
            notifyObserver();
        }
        changed = false;
    }

当需要更新时,需要先设置为true:

weatherData.setChanged(true);
weatherData.setWeatherData(10,"晴转多云",20);

观察者模式实现2——拉取数据

上面的实现都是被观察者向观察者推送数据,其实观察者也可利用getter()方法从被观察者中取出自己想要的数据

被观察者接口

不需要改变:

interface Subject {
    void registerObserver(Observer o);

    void unregisterObserver(Observer o);

    void notifyObserver();
}

观察者接口

修改Observer的update方法不添加参数:

interface Observer {
    void update();
}

被观察者实例

修改WeatherData在notifyObserver()方法中不传递参数给observer的update()方法,同时为属性添加getter()方法:

public class WeatherData implements Subject {

    private int lowTemperature;
    private String weather;
    private int highTemperature;
    private ArrayList<Observer> observers;

    public WeatherData() {
        observers = new ArrayList<>();
    }


    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void unregisterObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i > 0) {
            observers.remove(o);
        }
    }

    @Override
    public void notifyObserver() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.update();
        }
    }

    public int getLowTemperature() {
        return lowTemperature;
    }

    public String getWeather() {
        return weather;
    }

    public int getHighTemperature() {
        return highTemperature;
    }


    public void dataChanged() {
        notifyObserver();
    }

    public void setWeatherData(int lowTemperature, String weather, int highTemperature) {
        this.lowTemperature = lowTemperature;
        this.weather = weather;
        this.highTemperature = highTemperature;
        dataChanged();
    }
}

观察者实例

观察者实例内部维护被观察者接口,通过构造方法传入,当需要更新数据时进行数据类型转换再调用gettter()方法获取

class BeijingTv implements Observer {

    private Subject subject;

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

    @Override
    public void update() {
        if (subject instanceof WeatherData) {
            WeatherData weatherData= (WeatherData) subject;
            System.out.println("天气:" +weatherData.getWeather()+",最低气温:" + weatherData.getLowTemperature() + ",最高气温:" + weatherData.getHighTemperature());
        }
    }
}

通知过程

被观察者作为参数传入观察者内部注册:

WeatherData weatherData=new WeatherData();
BeijingTv beijingTv=new BeijingTv(weatherData);

当数据改变时,会通知各个观察者更新数据:

weatherData.setWeatherData(10,"晴转多云",20);

应该选哪个?

  • 推送数据实现:将获取到的数据推送给观察者,由观察者从中进行筛选出需要的数据进行更新
  • 拉取数据实现:由观察者自己请求相关数据,但这时可能被观察者并没有准备好对应的数据

根据需要选择相应的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值