设计模式 5分钟弄懂观察者模式

前言:观察者模式,顾名思义,是一种一对多的关系,由多个观察对象和被观察对象(主题)组成。主要作用是被观察者(主题)发生改变时,会主动通知观察者对象它自己发生了改变。而观察者对象则可以选择它感兴趣的主题进行观察。

 

1、简单介绍

这里举一个简单的例子来说明,在以前互联网还没有如此发达的时候,运营商(中国移动、中国电信、中国联通)会给我们提供天气预报服务,我们通过发送短信的方式向运营商订购指定城市的天气信息,订购成功以后,每当天气会发生变化时,我们都会收到来自运营商的天气变化的短信提示。而当我们不再需要天气信息的提示时,我们也可以向运营商发送指定的短信去取消我们的订阅,取消成功以后我们就再也不会收到来自运营商的天气信息的提醒,这其实就是一个最简单的观察者模式。在这里,我们就扮演着观察者的身份,观察我们感兴趣的信息,而运营商就是被观察对象(主题)我们也可以自行决定是否关注它,简单画个图:

图1:用户王五订阅了运营商的天气信息,所以每当有新的天气变化时,运营商都会给王五发送天气信息。而用户张三和李四则对天气信息不敢兴趣所以没有订阅天气信息,因此,运营商也不会给张三和李四发送天气信息。

图2:现在李四出门总是被雨淋,所以现在李四也想提前预知天气信息,故李四给运营商发送订阅天气预报的信息,现在运营商把李四也加入了他们的订阅用户列表,所以一有新的天气信息,就会下发给李四和王五。

图3:张三发现李四订阅了天气预报,自己也想体验一下,故张三给运营商发送了订阅信息,现在运营商把张三也加到了订阅用户列表,而王五觉得自己最近都不出门,天气信息对他而言已经不感兴趣,就取消了订阅,所以现在有新的天气变化时,张三和李四都能收到运营商发送的信息,而王五则不会收到天气信息了。

 

2、代码实现

现在用户和运营商之间的关系已经描述清楚,我们现在来看看类图吧:

WeatherSubject实现Subject接口,把自己变成一个被观察者(主题),里面的registerObserver()方法和removeObserver()是提供给观察者进行订阅和取消订阅使用的。

每一个观察者对象需实现Observer接口,Observer接口中的update()方法会在Subject的实现类的notification()方法中被调用,来通知观察者数据方法了变化。

2.1、先看看接口的定义:

/**
 * 主题的接口
 */
public interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notificationAll();
}

/**
 * 观察者的接口 update()给主题调用  告知观察者有新的消息
 */
public interface Observer {
    void update(String title, String message);
}

/**
 * 观察者对象必须实现的接口 用来显示信息
 */
public interface ShowMessages {
    void show(String title, String message);
}

2.2、Subject接口实现类:

package com.felix.demo;

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

public class WeatherSubject implements Subject {

    //用来存放订阅消息的观察者对象
    private List<Observer> list;
    private String title, messages;

    public WeatherSubject(){
        list = new ArrayList<>();
    }

    /**
     * 订阅消息的方法
     * @param observer 观察者对象
     */
    @Override
    public void addObserver(Observer observer) {
        list.add(observer);
    }

    /**
     * 取消订阅消息的方法
     * @param observer 观察者对象
     */
    @Override
    public void removeObserver(Observer observer) {
        list.remove(list.indexOf(observer));
    }

    /**
     * 通知观察者有新的天气信息
     */
    @Override
    public void notificationAll() {
        for (Observer observer : list){
            observer.update(title, messages);
        }
    }

    /**
     * 运营商设置天气信息
     */
    public void setWeatherData(String title, String messages){
        this.title = title;
        this.messages = messages;
        notificationAll();
    }
}

2.3、Observer接口实现类:

package com.felix.demo;

public class WangWuObserver implements Observer,ShowMessages {
    /**
     * 天气信息发生改变时被调用
     */
    @Override
    public void update(String title, String message) {
        show(title, message);
    }
    /**
     * 展示天气信息
     */
    @Override
    public void show(String title, String message) {
        System.out.println("王五 "  +"主题:"+ title + "\n内容:" + message);
    }
}

由于张三和李四的实现和王五都是相同的,故这就贴一下王五的代码,主要就是实现Observer接口并重写里面的update()方法,当有新的天气信息时,若王五订阅了运营商的天气信息,该方法就会被回调来告知王五天气信息发生了变化。

2.4、测试类

现在我们就来模拟图1、图2、图3的情况来看看测试类,首先是图1,王五订阅了天气信息,而张三和李四则没有:

package com.felix.demo;

public class Test {
    public static void main(String [] args){
        WeatherSubject subject = new WeatherSubject();

        //三个用户对象
        ZhangSanObserver zs = new ZhangSanObserver();
        LiSiObserver ls = new LiSiObserver();
        WangWuObserver ww = new WangWuObserver();

        //王五订阅了天气信息
        subject.addObserver(ww);

        //张三和李四未订阅天气信息
        //subject.addObserver(ls);
        //subject.addObserver(zs);

        //运营商发布天气信息
        //2018.08.01
        subject.setWeatherData("天气信息","您好,今天2018年08月01号," +
                "深圳天气晴,温度29℃---38℃,出门注意防晒。\n");
        //2018.08.02
        subject.setWeatherData("天气信息","您好,今天2018年08月02号," +
                "深圳天气多云转阵雨,温度28℃---36℃,出门请带伞。\n");
    }
}

运行结果:

OK,运行结果如上,仅王五收到了来自运营商发布的天气信息。现在来看看图2的情况,李四也对天气信息感兴趣,所以把自己也变成了观察者,订阅了运营商的天气信息,测试类:

package com.felix.demo;

public class Test {
    public static void main(String [] args){
        WeatherSubject subject = new WeatherSubject();

        //三个用户对象
        ZhangSanObserver zs = new ZhangSanObserver();
        LiSiObserver ls = new LiSiObserver();
        WangWuObserver ww = new WangWuObserver();

        //王五订阅了天气信息
        subject.addObserver(ww);

        //李四也订阅了天气信息
        subject.addObserver(ls);

        //张三未订阅天气信息
        //subject.addObserver(zs);

        //运营商发布天气信息
        //2018.08.03
        subject.setWeatherData("天气信息","您好,今天2018年08月03号," +
                "深圳天气多云,温度26℃---37℃,出门注意防晒。\n");
        //2018.08.04
        subject.setWeatherData("天气信息","您好,今天2018年08月04号," +
                "深圳天气小雨,温度28℃---35℃,出门请带雨伞。\n");
    }
}

运行结果:

正如我们所料,李四也收到了来自运营商发布的天气信息。现在来看看图3,王五对天气信息不敢兴趣了,而张三却想变成观察者,测试类:

package com.felix.demo;

public class Test {
    public static void main(String [] args){
        WeatherSubject subject = new WeatherSubject();

        //三个用户对象
        ZhangSanObserver zs = new ZhangSanObserver();
        LiSiObserver ls = new LiSiObserver();
        WangWuObserver ww = new WangWuObserver();

        //王五订阅了天气信息
        subject.addObserver(ww);

        //李四也订阅了天气信息
        subject.addObserver(ls);

        //张三也订阅了天气信息
        subject.addObserver(zs);

        //王五取消订阅天气信息
        subject.removeObserver(ww);

        //运营商发布天气信息
        //2018.08.05
        subject.setWeatherData("天气信息","您好,今天2018年08月05号," +
                "深圳天气晴,温度28℃---39℃,出门注意防晒。\n");
        //2018.08.06
        subject.setWeatherData("天气信息","您好,今天2018年08月06号," +
                "深圳天气多云转晴,温度26℃---38℃,出门注意防晒。\n");
    }
}

运行结果:

现在,王五再也不会收到来自运营商的推送,除非他再次订阅。

OK,到此观察者模式的基本思路和实现介绍完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值