Java编程思想(五)事件通知模式解耦过程

1,945 阅读7分钟

banner窄.png

铿然架构  |  作者  /  铿然一叶 这是铿然架构的第 76 篇原创文章

相关阅读:

JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(六)事件通知模式解耦过程
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃
HikariPool源码(二)设计思想借鉴
【极客源码】JetCache源码(一)开篇
【极客源码】JetCache源码(二)顶层视图
人在职场(一)IT大厂生存法则


1. 原始需求和目标

  • 需求
  • 对于一个android应用,当监测网络变化到wifi时,检查应用是否有新版本,如果有则下载并安装。

  • 目标
  • 通过这个简单的需求,看下如何通过事件通知模式一步步解耦,最终提供一个比较完美的解决方案。

    2. 迭代1

    需求看起来比较简单,监控下网络变化,如果从非wifi变化到wifi,则调用安装器进行版本检测和安装。其类结构如下:

    2.1. 实现代码

    public class NetworkListener {
    
        public void networkChanged(NetworkType from, NetworkType to) {
            if (from != null && to != null) {
                notifyInstaller(from, to);
            }
        }
    
        private void notifyInstaller(NetworkType from, NetworkType to) {
            if (to == NetworkType.WIFI && from != NetworkType.WIFI) {
                // 代码耦合,如果有新的逻辑依赖网络罗变化,则要则这里修改代码
                Installer installer = new Installer();
                if (installer.hasNewVersion()) {
                    installer.downloadApk();
                    installer.upgrade();
                }
            }
        }
    }
    
    class Installer {
        public boolean hasNewVersion() {
            System.out.println("has new version: true");
            return true;
        }
    
        public void downloadApk() {
            System.out.println("downloadAPk");
        }
    
        public void upgrade() {
            System.out.println("upgrade new version apk.");
        }
    }
    
    enum NetworkType {
        NONE,
        MOBILE,
        WIFI
    }
    

    2.2. 验证代码

    public class EntryDemo {
        public static void main(String[] args) {
            NetworkListener listener = new NetworkListener();
            listener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
        }
    }
    

    2.3. 输出

    has new version: true
    downloadAPk
    upgrade new version apk.
    

    至此,需求实现,没几分钟就搞定,但这种实现方式将Installer和NetworkListener耦合,如果将来有新的业务也需要根据网络变化进行处理,则要在NetworkListener内部修改代码,因此需要对这点解耦。

    3. 迭代2

    迭代2类结构如下:

    1.新增NetworkChangedEventSubscriber接口,可订阅NetworkChangedEvent

    2.NetworkListener可添加NetworkChangedEventSubscriber,当发生NetworkChangedEvent时则通知它。

    3.1. 实现代码

    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * @ClassName NetworkListener
     * @Description
     * @Author 铿然一叶
     * @Date 2020/5/31 15:29
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class NetworkListener {
    
        private Set<NetworkChangedEventSubscriber> subscribers;
    
        public NetworkListener() {
            subscribers = new CopyOnWriteArraySet<>();
        }
    
        public void networkChanged(NetworkType from, NetworkType to) {
            if (subscribers != null) {
                NetworkChangedEvent event = new NetworkChangedEvent(from, to);
                for (NetworkChangedEventSubscriber subscriber: subscribers) {
                    subscriber.notifyNetworkChanged(event);
                }
            }
        }
    
        // 可以添加多个Subscriber
        public void addNetworkChangedEventSubscriber(NetworkChangedEventSubscriber subscriber) {
            subscribers.add(subscriber);
        }
    
        // 封装网络变化事件
        public static class NetworkChangedEvent {
            NetworkType from;
            NetworkType to;
    
            public NetworkChangedEvent(NetworkType from, NetworkType to) {
                this.from = from;
                this.to = to;
            }
        }
    
        // 定义Subscriber接口
        public interface NetworkChangedEventSubscriber {
            void notifyNetworkChanged(NetworkChangedEvent event);
        }
    }
    
    // 实现Subscriber接口
    class Installer implements NetworkListener.NetworkChangedEventSubscriber {
        @Override
        public void notifyNetworkChanged(NetworkListener.NetworkChangedEvent event) {
            if (event != null && event.from != null && event.to != null) {
                if (event.to == NetworkType.WIFI && event.from != NetworkType.WIFI) {
                    if (hasNewVersion()) {
                        downloadApk();
                        upgrade();
                    }
                }
            }
        }
        public boolean hasNewVersion() {
            System.out.println("has new version: true");
            return true;
        }
    
        public void downloadApk() {
            System.out.println("downloadAPk");
        }
    
        public void upgrade() {
            System.out.println("upgrade new version apk.");
        }
    }
    
    enum NetworkType {
        NONE,
        MOBILE,
        WIFI
    }
    

    3.2. 验证代码

    public class EntryDemo {
        public static void main(String[] args) {
            NetworkListener networkListener = new NetworkListener();
            // 已经解耦,只要添加新的subscriber则可
            networkListener.addNetworkChangedEventSubscriber(new Installer());
    
            networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
        }
    }
    

    这种方式的缺点是必须获得networkListener实例才能添加listener,导致每次添加新的listener都要在创建NetworkListener的地方修改代码。

    4. 迭代3

    迭代3类结构如下: 新增NetworkChangedEventRegistry来负责注册和获取NetworkChangedEventSubscriber,使得注册Subscriber和获取Subscriber解耦。

    4.1. 实现代码

    4.1.1. NetworkListener.java

    import java.util.Set;
    
    /**
     * @ClassName NetworkListener
     * @Description
     * @Author 铿然一叶
     * @Date 2020/5/31 15:55
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class NetworkListener {
    
        private Set<NetworkChangedEventSubscriber> subscribers;
    
        public void networkChanged(NetworkType from, NetworkType to) {
            NetworkChangedEvent event = new NetworkChangedEvent(from, to);
            // 和subscriber解耦
            Set<NetworkChangedEventSubscriber> subscribers = NetworkChangedEventRegistry.getInstance().getSubscribers();
            if (subscribers != null) {
                for (NetworkChangedEventSubscriber subscriber : subscribers) {
                    subscriber.notifyNetworkChanged(event);
                }
            }
        }
    
        public static class NetworkChangedEvent {
            NetworkType from;
            NetworkType to;
    
            public NetworkChangedEvent(NetworkType from, NetworkType to) {
                this.from = from;
                this.to = to;
            }
        }
    
        public interface NetworkChangedEventSubscriber {
            void notifyNetworkChanged(NetworkChangedEvent event);
        }
    }
    
    enum NetworkType {
        NONE,
        MOBILE,
        WIFI
    }
    

    4.1.2. Installer.java

    public class Installer implements NetworkListener.NetworkChangedEventSubscriber {
        @Override
        public void notifyNetworkChanged(NetworkListener.NetworkChangedEvent event) {
            if (event != null && event.from != null && event.to != null) {
                if (event.to == NetworkType.WIFI && event.from != NetworkType.WIFI) {
                    if (hasNewVersion()) {
                        downloadApk();
                        upgrade();
                    }
                }
            }
        }
        public boolean hasNewVersion() {
            System.out.println("has new version: true");
            return true;
        }
    
        public void downloadApk() {
            System.out.println("downloadAPk");
        }
    
        public void upgrade() {
            System.out.println("upgrade new version apk.");
        }
    }
    

    4.1.3. NetworkChangedEventRegistry.java

    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * @ClassName NetworkChangedEventRegistry
     * @Description
     * @Author 铿然一叶
     * @Date 2020/5/31 15:58
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class NetworkChangedEventRegistry {
        // 单例模式写法
        private volatile static NetworkChangedEventRegistry registry;
    
        private Set<NetworkListener.NetworkChangedEventSubscriber> subscribers;
    
        public NetworkChangedEventRegistry() {
            subscribers = new CopyOnWriteArraySet<>();
        }
    
        // 单例模式
        public static NetworkChangedEventRegistry getInstance() {
            if (registry == null) {
                synchronized (NetworkChangedEventRegistry.class) {
                    if (registry == null) {
                        registry = new NetworkChangedEventRegistry();
                    }
                }
            }
            return registry;
        }
    
        public void register(NetworkListener.NetworkChangedEventSubscriber subscriber) {
            if (subscriber != null) {
                subscribers.add(subscriber);
            }
        }
    
        public void unregister(NetworkListener.NetworkChangedEventSubscriber subscriber) {
            if (subscriber != null) {
                subscribers.remove(subscriber);
            }
        }
    
        public Set<NetworkListener.NetworkChangedEventSubscriber> getSubscribers() {
            return subscribers;
        }
    }
    

    4.2. 验证代码

    public class EntryDemo {
        public static void main(String[] args) {
            // 通过两个独立方法来说明subscriber的注册和listenr已经解耦
            register();
            networkChanged();
        }
    
        private static void register() {
            NetworkChangedEventRegistry.getInstance().register(new Installer());
        }
    
        private static void networkChanged() {
            NetworkListener networkListener = new NetworkListener();
            networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
        }
    }
    

    到这里,实现已经几近完美,从解耦角度来看没有什么可挑剔的,但软件的质量属性不仅仅是解耦,还有可靠性等等。

    而在当前实现中,通知subscriber是同步模式,如果有多个subscriber,其中一个subscriber的执行时间很长,排在它后面的subscriber收到通知就不及时,可能出现收到通知时网络再次发生变化,已经不再是WIFI网络。

    因此为了及时通知每个subscriber,要使用异步模式,但可能有的可需要同步,有的需要异步,于是我们引入Dispatcher来解决这个问题。

    5. 迭代4

    迭代4类结构如下:

    新增Dispatcher来派发event给Subscriber,通过不同的实现类可决定是同步还是异步方式。

    5.1. 实现代码

    5.1.1. NetworkListener.java

    import java.util.Set;
    
    /**
     * @ClassName NetworkListener
     * @Description
     * @Author 铿然一叶
     * @Date 2020/5/31 16:26
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class NetworkListener {
    
        private Dispatcher dispatcher;
    
        // 通过依赖注入解耦,由调用者传入具体实现类
        public NetworkListener(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }
    
        public void networkChanged(NetworkType from, NetworkType to) {
            NetworkChangedEvent event = new NetworkChangedEvent(from, to);
            Set<NetworkChangedEventSubscriber> subscribers = NetworkChangedEventRegistry.getInstance().getSubscribers();
            if (subscribers != null) {
                for (NetworkChangedEventSubscriber subscriber : subscribers) {
                    // 由Dispatcher来派发事件
                    dispatcher.distribute(subscriber, event);
                }
            }
        }
    
        public static class NetworkChangedEvent {
            NetworkType from;
            NetworkType to;
    
            public NetworkChangedEvent(NetworkType from, NetworkType to) {
                this.from = from;
                this.to = to;
            }
        }
    
        public interface NetworkChangedEventSubscriber {
            void notifyNetworkChanged(NetworkChangedEvent event);
        }
    }
    
    enum NetworkType {
        NONE,
        MOBILE,
        WIFI
    }
    

    5.1.2. Dispatcher

    public interface Dispatcher {
        void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event);
    }
    
    /**
     * @ClassName SyncDispatcher
     * @Description 同步Dispatcher
     * @Author 铿然一叶
     * @Date 2020/5/31 16:37
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class SyncDispatcher implements Dispatcher {
        @Override
        public void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event) {
            if (subscriber != null && event != null) {
                System.out.println("sync distribute start.");
                subscriber.notifyNetworkChanged(event);
                System.out.println("sync distribute end.");
            }
        }
    }
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @ClassName AsyncDispatcher
     * @Description 异步Dispatcher
     * @Author 铿然一叶
     * @Date 2020/5/31 16:38
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class AsyncDispatcher implements Dispatcher {
    
        private ExecutorService executorService;
    
        public AsyncDispatcher() {
            // 通过线程池提高性能,异步派发的线程处理方式可以自行根据需要修改
            executorService = Executors.newSingleThreadExecutor();
        }
        @Override
        public void distribute(NetworkListener.NetworkChangedEventSubscriber subscriber, NetworkListener.NetworkChangedEvent event) {
            if (subscriber != null && event != null) {
                System.out.println("async distribute start.");
                executorService.execute(()->subscriber.notifyNetworkChanged(event));
                System.out.println("async distribute end.");
            }
        }
    }
    

    5.2. 验证代码

    /**
     * @ClassName EntryDemo
     * @Description
     * @Author 铿然一叶
     * @Date 2020/5/31 16:43
     * @Version 1.0
     * 掘金:https://juejin.im/user/3544481219739870
     **/
    public class EntryDemo {
        public static void main(String[] args) {
            // 通过两个独立方法来说明subscriber的注册和listenr已经解耦
            register();
            syncDistribute();
            asyncDistribute();
        }
    
        private static void register() {
            NetworkChangedEventRegistry.getInstance().register(new Installer());
        }
    
        private static void syncDistribute() {
            Dispatcher dispatcher = new SyncDispatcher();
            NetworkListener networkListener = new NetworkListener(dispatcher);
            networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
        }
    
        private static void asyncDistribute() {
            Dispatcher dispatcher = new AsyncDispatcher();
            NetworkListener networkListener = new NetworkListener(dispatcher);
            networkListener.networkChanged(NetworkType.MOBILE, NetworkType.WIFI);
        }
    }
    

    5.3. 输出

    sync distribute start.
    has new version: true
    downloadAPk
    upgrade new version apk.
    sync distribute end.
    async distribute start.
    async distribute end.
    has new version: true
    downloadAPk
    upgrade new version apk.
    

    可以看到同步模式和异步模式的差别,异步模式派发后不需要等待subscriber执行,派发立即就结束了,subscriber通过线程执行。

    6. 总结

    最终事件通知模式抽象后的类结构为:

    通过本章我们掌握了如下知识点:

    1. 事件通知模式实现时需要考虑的几个要点,以及核心类。
    2. 通过依赖注入实现解耦是一种经典解耦模式,要善用。(参考Dispatcher的注入)
    3. 软件质量属性不仅仅是解耦,可扩展,其他属性也要考虑,例如可靠性。

    end.


    <--阅过留痕,左边点赞!