【设计模式系列7】详解观察者模式pull和push两种写法

java设计模式之观察者模式

  • 设计模式系列总览
  • 什么是观察者模式
  • 观察者模式示例
  • JDK观察者模式使用
  • java.util.Observable的黑暗面
  • 观察者模式的有优点
  • 观察者模式的有缺点

设计模式系列总览

设计模式 飞机票
三大工厂模式 登机入口
策略模式 登机入口
委派模式 登机入口
模板方法模式 登机入口
观察者模式 登机入口
单例模式 登机入口
原型模式 登机入口
代理模式 登机入口
装饰者模式 登机入口
适配器模式 登机入口
建造者模式 登机入口
责任链模式 登机入口
享元模式 登机入口
组合模式 登机入口
门面模式 登机入口
桥接模式 登机入口
中介者模式 登机入口
迭代器模式 登机入口
状态模式 登机入口
解释器模式 登机入口
备忘录模式 登机入口
命令模式 登机入口
访问者模式 登机入口
软件设计7大原则和设计模式总结 登机入口

什么是观察者模式

观察者模式(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才会进行通知
【设计模式系列7】详解观察者模式pull和push两种写法_第1张图片
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);
    }
}

输出结果为如下:
【设计模式系列7】详解观察者模式pull和push两种写法_第2张图片
说明发表动态之后朋友收到了通知,且拿到了动态相关数据,实现了观察者模式pull(拉)数据

java.util.Observable的黑暗面

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

观察者模式的有优点

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

观察者模式的有缺点

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

你可能感兴趣的:(设计模式,java,设计模式)