jdk观察者模式的黑暗面

什么是观察者模式

观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式,主要用 于在关联行为之间建立一套触发机制的场景

观察者模式示例

假如我们需要从气象台获取天气信息用作展示,当天气信息发生改变之后,我们也需要同步发生改变。
1、建立一个Subject接口

package com.zwx.design.pattern.observe.headfirst;

public interface Subject {
    void registerObserver(Observer o);//注册观察对象
    void removeObserver(Observer o);//移除观察对象
    void notifyObservers();//通知观察对象
}

2、建立一个WeatherData(被观察者)实现Subject接口(面向接口编程会更易于扩展,这里也可以不用Subject接口,直接建立WeatherData实现)

package com.zwx.design.pattern.observe.headfirst;

import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {
    private List<Observer> observers;

    private float temperature;//温度
    private float humidity;//湿度
    private float pressure;//气压

    public void setMessurements(float temperature,float humidity,float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        notifyObservers();//气温信息发生变化时,通知所有观察者
    }

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

    /**
     * 注册观察者
     * @param o
     */
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    /**
     * 移除观察者
     * @param o
     */
    @Override
    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if(i >= 0){
            observers.remove(i);
        }

    }

    /**
     * 通知所有观察者
     */
    @Override
    public void notifyObservers() {
        for (Observer observer : observers){
            observer.update(temperature,humidity,pressure);
        }
    }
}

3、建立一个***观察者***接口

package com.zwx.design.pattern.observe.headfirst;

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

这个接口只定义了一个update方法,用于更新气象数据
4、建立一个WeatherDisplay(观察者)类实现Observer接口

package com.zwx.design.pattern.observe.headfirst;

public class WeatherDisplay implements Observer {
    private Subject subject;
    private float temperature;//温度
    private float humidity;//湿度
    private float pressure;//气压

    public WeatherDisplay(Subject subject) {//注册监听对象
        this.subject = subject;
        subject.registerObserver(this);
    }

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

    public void display(){
        System.out.println("当前最新的温度为:" + temperature + ",湿度为:" + humidity +
                ",气压为:" + pressure);
    }
}

5、最后再写一个测试类测试:

package com.zwx.design.pattern.observe.headfirst;

public class WeatherDataTest {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();//气象数据即被观察者
        WeatherDisplay weatherDisplay = new WeatherDisplay(weatherData);//天气展示即观察者
        weatherData.setMessurements(37.2f,80f,32.5f);
        weatherDisplay.display();//天气展示
    }
}

最后输出结果为:
在这里插入图片描述
说明被观察者WeatherData气象发生变化时,观察者WeatherDisplay也及时获得了最新的数据变化,从而实现了一个简单的观察者模式!

上面的示例中存在一种问题,那就是数据是被push(推)过来的,也就是说不管观察者想不想要,只要气象发生变化了,被观察者就会把数据push(推)给观察者,那么能不能实现让观察者主动去pull(拉)呢?答案是肯定的,上面我们只要把WeatherData中每个数据都提供getter方法,然后再稍作改进就可以实现!
JDK中自带实现了观察者模式,并且实现了push(推)和pull(拉)两种类型,接下来就让我们用JDK自带的观察者模式来实现pull(拉)的场景。

JDK观察者模式使用

假设我们在空间中发了一条动态,需要把这条动态推送给好友
1、首先建立一个Zone类(被观察者)实现Observable类

package com.zwx.design.pattern.observe.trendsNotice;

import java.util.Observable;

public class Zone extends Observable {
    public void publishTrends(Trends trends){
        System.out.println(trends.getNickName() + "发表了一个动态【" + trends.getContent() + "】" );
        setChanged();//仅作标识用
        notifyObservers(trends);//通知所有观察者
    }
}

注意这里的Observable是java.util包下面的,这个类里面定义了一个发表动态的方法。这里setChanged()仅仅只是把一个改变标识设置为true
在这里插入图片描述
之所以要这一个,是因为JDK中的notifyObservers方法中会判断这个标识是否为true,为true才会进行通知
在这里插入图片描述
2、定义一个动态类

package com.zwx.design.pattern.observe.trendsNotice;

public class Trends {
    private String nickName;//发表动态的用户暱称
    private String content;//发表的动态内容

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

3、定义一个Friends类(观察者),实现Observer接口

package com.zwx.design.pattern.observe.trendsNotice;

import java.util.Observable;
import java.util.Observer;

public class Friends implements Observer {
    private String friendName;

    public String getFriendName() {
        return friendName;
    }

    public void setFriendName(String friendName) {
        this.friendName = friendName;
    }

    @Override
    public void update(Observable o, Object arg) {
     	Trends trends = new Trends();
        if(null != arg && arg instanceof Trends){
            trends = (Trends)arg;
        }
        System.out.println(this.getFriendName() + ",您好!您收到了来自" + trends.getNickName() +
                "的一条动态【" + trends.getContent() + "】" + "快去点赞吧!");

    }
}

注意这里的update方法中的第2个参数,如果上面Zone类中的方法publishTrends方法中调用的notifyObservers方法不传入参数trends,这里拿到的就是空,也就是相当于实现了push(推),传入了参数我们update中就能拿到数据对象,这时候我们就可以主动去pull(拉)数据,只选择自己想要的数据进行展示或者处理
4、最后写一个测试类测试

package com.zwx.design.pattern.observe.trendsNotice;

public class ObserverTest {
    public static void main(String[] args) {
        Zone zone = new Zone();
        Friends friends = new Friends();
        friends.setFriendName("张三丰");

        Trends trends = new Trends();
        trends.setNickName("张无忌");
        trends.setContent("祝太师傅长命百岁!");
        zone.addObserver(friends);
        zone.publishTrends(trends);
    }
}

输出结果为如下:
在这里插入图片描述
说明发表动态之后朋友收到了通知,且拿到了动态相关数据,实现了观察者模式pull(拉)数据

java.util.Observable的黑暗面

1、Observable是一个类而不是一个接口,所以就限制了它的使用和服用,如果某类同时想具有Observable类和另一个超类的行为,就会有问题,毕竟java不支持多继承。
2、Observable将关键的方法保护起来了,比如setChanged()方法,这意味着除非我们继承自Observable,否则无法创建Observable实例并组合到我们自己的对象中来,这个设计违反了设计原则:多用组合,少用继承。

观察者模式的有优点

1、观察者和被观察者之间建立了一个抽象的耦合。
2、观察者模式支持广播通信

观察者模式的有缺点

1、观察者之间有过多的细节依赖、提高时间消耗及程序的复杂度。 2、使用要得当,要避免循环调用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章