Android面试题--设计模式之观察者模式的通俗易懂实现 与发布/订阅框架有区别解析

观察者模式

观察者模式

在前面三篇文章聊了构建者模式、单例模式、工厂模式,这三种都是属于创建型模式,接下来几篇文章来聊聊行为型模式里的一些设计模式;这篇文章的主题是观察者模式:在对象之间定义了一对多的关系,当一个对象发生改变时,依赖它的对象将收到通知并自动更新

怎么形象的理解这句话呢?

天气预报这类的应用大家都有用到过,现在有一个实时获取天气信息的目标类A,然后还有需要用到天气信息在界面显示的观察者类B,观察者类C,观察者类D等,这些观察者类要想目标类A在获取到数据后通知自己,就需要将自己注册到目标类A;然后目标类A在获取数据后就可以调用观察者类的更新方法,这样观察者类就能实时更新UI了

所以就可以知道观察者模式的实现方式就是:先定义观察者和目标的基类,目标需要提供维护观察者的方法(比如注册和解除注册),观察者需要提供更新接口;接下来具体的观察者和具体的目标就继承基类,具体观察者将自己注册到具体目标类中,这样当具体目标类发生变化时,就可以调用已注册的观察者的更新方法

与发布订阅模式区别

说到这里有没有发现它跟我们平时开发常用的一些发布-订阅框架很类似,但是它们两其实是有具体差异的;发布订阅框架逻辑:订阅者把自己想注册的事件注册到调度中心,当事件触发时,发布者将事件发布到订阅中心,由调度中心统一调度注册的订阅者

可以看到观察者模式和发布订阅模式的区别:

发布订阅模式统一由调度中心调度,而观察者模式由具体目标进行调度

发布订阅模式中订阅者和发布者不存在耦合,而观察者模式中观察者和目标存在耦合关系

其实也可以将发布订阅看做是观察者模式的一个变种,优化

实现

这里假设这么一个场景:有三个用户Tom、Lina、jams都关注了同一个音乐电台;当音乐电台有音乐发布时,将消息推送给三个用户;然后有一个用户认为这个电台音乐不太符合自己的风格,就决定取消关注;最后这个电台只能将消息推送给其余两个用户

定义观察者接口

/**
 * Author:mango
 * Time: 2019/4/9 11:06
 * Desc: TODO(观察者接口)
 */
public interface Observer {

    /**
     * 观察者接收数据更新
     * 参数以具体数据类型为准
     */
    void updateData(String msg);
}

这个接口由具体观察者实现

定义目标接口

/**
 * Author:mango
 * Time: 2019/4/9 11:05
 * Desc: TODO(目标接口)
 */
public interface Observedable {

    /**
     * 观察者调用:将自己注册到目标类
     * @param observer
     */
    void registerOberver(Observer observer);

    /**
     * 观察者调用:解除注册
     * @param observer
     */
    void unregisterOberver(Observer observer);

    /**
     * 目标类发布事件
     * @param msg
     */
    void pushEvent(String msg);
}

这个接口由目标类实现,也就是谁被观察由谁实现

定义具体观察者

/**
 * Author:mango
 * Time: 2019/4/9 11:12
 * Desc: TODO(具体观察者)
 */
public class User implements Observer {

    private String userName;
    
    private String msg;

    public User(String userName, String msg) {
        this.userName = userName;
        this.msg = msg;
    }

    @Override
    public void updateData(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }
}

定义具体目标

/**
 * Author:mango
 * Time: 2019/4/9 11:14
 * Desc: TODO(目标对象)
 */
public class MusicRadio implements Observedable {

    private List<Observer> userList;

    public MusicRadio() {
        userList = new ArrayList<>();
    }

    /**
     * 注册观察者
     * @param observer
     */
    @Override
    public void registerOberver(Observer observer) {
        userList.add(observer);
    }

    /**
     * 注销观察者
     * @param observer
     */
    @Override
    public void unregisterOberver(Observer observer) {
        if (userList.contains(observer)) {
            userList.remove(observer);
            observer.updateData("");
        }
    }

    /**
     * 向观察者推送消息
     * @param msg
     */
    @Override
    public void pushEvent(String msg) {
        for (Observer observer : userList) {
            observer.updateData(msg);
        }
    }

    /**
     * 内部模拟消息产生,当然实际开发中这里可能是通过网络请求向服务器获取数据
     * 或者服务器主动推送到这里
     * @param msg
     */
    public void produceMusic(String msg){
        pushEvent(msg);
    }

}



可以看到被观察对象,也就是目标对象内部维护了一个集合用来保存已经注册的观察者,只要有消息来了,就将消息推送给观察者

测试

    public static void main(String[] args){

        //被观察对象
        Observedable musicRadio = new MusicRadio();

        /**
         * 实例化三个观察者
         * 也就是三个用户,他们都观察musicRadio
         */
        User tom = new User("Tom");
        User lina = new User("Lina");
        User jams = new User("Jams");

        /**
         * 注册观察者
         */
        musicRadio.registerOberver(tom);
        musicRadio.registerOberver(lina);
        musicRadio.registerOberver(jams);

        /**
         * 创作了一首音乐,musicRadio内部将其消息推送给三个观察者
         */
        musicRadio.pushEvent("春天");

        //打印三个用户,看是否接收到消息了
        System.out.print(tom+"\n");
        System.out.print(lina+"\n");
        System.out.print(jams+"\n");

        System.out.print("=====================" + "\n");
        //jams决定取消关注musicRadio
        musicRadio.unregisterOberver(jams);

        /**
         * 创作了一首音乐,musicRadio内部将其消息推送给剩下的两个观察者
         */
        musicRadio.pushEvent("时光春晓");

        //打印三个用户,看谁接收到消息了
        System.out.print(tom+"\n");
        System.out.print(lina+"\n");
        System.out.print(jams+"\n");
    }


User{userName='Tom', msg='春天'}
User{userName='Lina', msg='春天'}
User{userName='Jams', msg='春天'}
=====================
User{userName='Tom', msg='时光春晓'}
User{userName='Lina', msg='时光春晓'}
User{userName='Jams', msg=''}

这里定义了三个用户,都关注了MusicRadio,然后MusicRadio推送了一首音乐,通过打印日志看到三个用户都接收到消息了;
当用户jams取消关注后,MusicRadio再推送音乐后就只能推送给剩下两个用户了

总结

通过面向接口的开发,实现松耦合,不论观察者是谁,怎么变,被观察者不需要修改;当被观察者变化时,比如从MusicRadio变成BookRadio,也只需要修改一处;这样就能极大增强程序的可扩展性;在Android中观察者模式的使用还是很多的,比如View点击事件的监听,就是一个注册观察者的过程;广播的使用也是一样的道理;当然生活中观察者模式的使用就很多了,比如你定闹钟,每天早上到了7点,它就会把你吵醒,这里你就是观察者,闹钟就是被观察者;多了解设计模式,不光对程序设计有好处,说不定对你的生活也能有意想不到的效果
————————————————
版权声明:本文为CSDN博主「渔夫在划桨」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_30993595/article/details/89138554

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值