美文网首页Android开发Android开发经验谈Android技术知识
Java设计模式 | 观察者模式解析与实战

Java设计模式 | 观察者模式解析与实战

作者: 凌川江雪 | 来源:发表于2020-04-10 05:07 被阅读0次

    概述

    • 观察者模式是一个使用率非常高的模式,
      它最常用的地方是 GUI 系统、订阅——发布系统。

    • 这个模式的一个重要作用就是解耦
      被观察者观察者解耦,
      使得它们之间的依赖性更小,甚至做到毫无依赖。

    • 以GUI系统来说,应用的UI具有易变性,
      尤其是前期随着业务的改变或者产品的需求修改,
      应用界面也会经常性变化,但是业务逻辑基本变化不大,
      此时,GUI系统需要一套机制来应对这种情况,
      使得UI层具体的业务逻辑解耦,观察者模式此时就派上用场了。

    !!!
    因为观察者仅负责调度被观察者更新方法
    并提供一个业务数据给被观察者
    被观察者具体实现更新方法【可以实现UI、数据更新】,
    更新方法具体实现的内容观察者业务逻辑基本毫无依赖!

    定义

    定义对象间一种一对多依赖关系
    使得每当一个对象改变状态,
    则所有依赖于它的对象都会得到通知并被自动更新

    使用场景

    ●关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系;
    ●事件多级触发场景;
    ●跨系统的消息交换场景,如消息队列事件总线处理机制

    UML类图

    ●Subject:抽象主题,也就是被观察(Observable)的角色,抽象主题角色把所有观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
    ●ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发出通知,具体主题角色又叫做具体被观察者(Concrete Observable)角色。
    ●Observer:抽象观察者,该角色是观察者的抽象类,它定义了一个更新接口,使得在得到主题的更改通知时更新自己。

    观察者模式实现思路总结

    观察者接口准备更新(数据或UI的)方法;
    被观察者接口准备三个抽象方法;

    观察者实现类具体实现更新逻辑,可以有参数,参数为更新需要的数据;

    被观察者实现类准备一个观察者List以及实现三个方法:
    1.观察者注册方法:
    参数为某观察者,功能是把观察者参数加到观察者List中;
    2.注销观察者方法:
    参数为某观察者,功能是把观察者参数从观察者List中移除;
    3.通知观察者方法:无参数或者把需要通知的数据作为参数,
    功能是遍历所有已注册的观察者,
    即遍历 注册添加到 观察者List中的观察者,逐个调用List中所有观察者的更新方法;即一次性更新所有已注册的观察者!

    使用时,
    实例化一个被观察者和若干个观察者,
    将所有观察者注册到被观察者处,
    调用被观察者的通知方法,一次性更新所有已注册的观察者!

    案例

    来自 Android | TCP的C(Java|Android)/S(Java)通信实战经典聊天室案例(文末附本案例代码实现概述、观察者模式实现小结)

    • 准备一个消息队列,
      每一个Client发送过来的消息,
      都会被加入到队列当中去,
      队列中默认有一个子线程,
      专门从队列中,死循环,不断去取数据(取出队列的队头),
      取到数据就做相关处理,比如分发给其他的socket;
    /**
     * <pre>
     *     desc   :每一个Client发送过来的消息,
     *             都会被加入到队列当中去,
     *             队列中默认有一个子线程,
     *             专门从队列中,死循环,不断去取数据,
     *             取到数据就做相关处理,比如分发给其他的socket;
     * </pre>
     */
    public class MsgPool {
    
        private static MsgPool mInstance = new MsgPool();
    
        /*
            这里默认消息是String类型,
            或者可以自行封装一个Model 类,存储更详细的信息
    
            block n.块; 街区;障碍物,阻碍
            顾名思义,这是一个阻塞的队列,当有消息过来时,就把消息发送给这个队列,
            这边会起一个线程专门从队列里面去取消息,
            如果队列中没有消息,就会阻塞在原地
         */
        private LinkedBlockingQueue<String> mQueue = new LinkedBlockingQueue<>();
    
        public static MsgPool getInstance() {
            return mInstance;
        }
    
        private MsgPool() {
        }
    
        //这是一个阻塞的队列,
        // 当有消息过来时,即客户端接收到消息时,
        // 就把消息发送(添加)到这个队列中
        //现在所有的客户端都可以发送消息到这个队列中
        public void sendMsg(String msg) {
            try {
                mQueue.put(msg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        //要一早就调用本方法,
        // 启动这个读取消息的线程,在后台不断运行
        public void start() {
            //开启一个线程去读队列的数据
            new Thread() {
                @Override
                public void run() {
                    //无限循环读取信息
                    while (true) {
                        try {
                            //取出并移除队头;没有消息时,take()是阻塞的
                            String msg = mQueue.take();
                            notifyMsgComing(msg);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
        }
    
        //被观察者方法,遍历所有已注册的观察者,一次性通知更新
        private void notifyMsgComing(String msg) {
            for (MsgComingListener listener : mListeners) {
                listener.onMsgComing(msg);
            }
        }
    
        //观察者接口
        public interface MsgComingListener {
            void onMsgComing(String msg);//更新方法
        }
    
        //被观察者,存放观察者
        private List<MsgComingListener> mListeners = new ArrayList<>();
    
        //被观察者方法,添加观察者到列表
        public void addMsgComingListener(MsgComingListener listener) {
            mListeners.add(listener);
        }
    }
    

    案例小结:
    所有的客户端都可发送消息到队列中,
    然后所有的客户端都在等待
    消息队列的消息新增(mQueue.put())这个时刻,
    消息队列一新增消息,
    即一接收到某个客户端发送过来消息(mQueue.put()),
    则消息都会一次性转发给所有客户端,
    所以这里涉及到一个观察者设计模式,
    消息队列(MsgPool)或消息(Msg)是被观察者,
    所有客户端处理线程(ClientTask)都是观察者









    参考:

    • 《Android源码设计模式解析与实战》
    • 慕课网

    相关文章

      网友评论

        本文标题:Java设计模式 | 观察者模式解析与实战

        本文链接:https://www.haomeiwen.com/subject/dnzkmhtx.html