EventBus源码分析​(四):EventBus中的几种设计模式

QuibblerQuibbler 2021-10-13 约 12 分钟 1981 次阅读

EventBus源码分析(四):EventBus中的几种设计模式

再看一下EventBus,总结一下其中几个重要的设计模式,也是最常见的几种设计模式:单例模式、建造者模式、观察者模式。多看源码的好处之一就是能从优秀的代码里活学活用。

1、单例模式

单例模式(Singleton Pattern)是设计模式中最简单也是最常见的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。

通常用一个私有静态变量存储这个唯一的实例,而且有的源码中还会加上volatile关键字修饰:

static volatile EventBus defaultInstance;

类提供了一种访问其唯一的对象的方式,可以直接访问类的实例,不需要再实例化该类的对象。这个方法也是实现单例模式的关键,EventBus中提供getDefault()方法获取全局唯一的EventBus单例,这种单例使用的是双检锁(DCL)方式,实现单例模式的方法参考单例的五种方式一文。

/** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

有了getDefault()方法,开发者就可以随处随地的获取EventBus实例,进行发送事件、注册订阅者等操作。

EventBus.getDefault().postSticky(new MessageEvent("Hello EventBus"));

而Kotlin提供了更加方便的单例实现,使用object关键词创建实例,见《对象声明:object》一文。

2、建造者模式

建造者模式(Builder Pattern)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。一个Builder类会一步一步构造最终的对象。该Builder类是独立于其他对象的。

构造EventBus实例就用到了建造者模式,构造EventBus实例的构造方法用到了EventBusBuilder这个构造类。

EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        ...
    }

借助EventBusBuilder类构造配置信息,提供一系列的设置方法。而且这些方法返回建造者自身,可以方便的实现链式调用,对构造对象进行设置的方法调用一气呵成。

/** Default: true */
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }
    
    /** Default: true */
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }
    
    /** Default: true */
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }
    
    /** Default: true */
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }
    ...

建造者Builder类通常还会提供一个build()方法,当一切都设置完成,调用该方法生成最终的对象实例。如EventBusBuilder中的build()方法:

/** Builds an EventBus based on the current configuration. */
    public EventBus build() {
        return new EventBus(this);
    }

还可以用EventBusBuilder中的installDefaultEventBus()方法先初始化EventBus库默认的实例:

public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

借助EventBus中的EventBusBuilder建造者,开发者可以自行定义EventBus行为属性,详见EventBus源码分析(一):构造及订阅一文第1.3节。

3、观察者模式

观察者模式(Observer Pattern)定义了一种一对多的依赖关系,让多个观察者对象同时监听某一对象。当被观察的对象发生修改时,会自动通知所有观察它的对象。观察者模式属于行为型模式。此设计模式最重要的作用就是解耦!将观察者与被观察者解耦,使得它们之间的依赖性更小。

EventBus就是使用了观察者模式,使代码间相互解耦。当发出消息时,通知所有注册的观察者(订阅者)。注册的订阅对象,其实就是观察者。

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

新的事件发送,就通知所有的观察者:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        ...
        subscriptions = subscriptionsByEventType.get(eventClass);
        for (Subscription subscription : subscriptions) {
            ...
            postToSubscription(subscription, event, postingState.isMainThread);
            ...
        }
    }

核心关系维护在两个Map映射中:subscriptionsByEventType和typesBySubscriber,详见EventBus源码分析(一):构造及订阅一文第2.1节。

设计模式不是纸上谈兵,曾看过几本关于设计模式的书:《设计模式之禅》、《Head First设计模式》、《大话设计模式》、《Android源码设计模式》等,最大的体会是设计模式属于经验之谈,是前人对于编程的一套经验总结,几十种设计模式硬背倒不难,但要灵活运用,唯手熟尔。

相关博客:

        设计模式的六大原则和23种设计模式

单例的五种方式

相关推荐

精选
Airbnb开源项目
开源

Airbnb开源项目

Airbnb Engineering & Data ScienceAirflow Use Apache Airflow (incubating) to author workflows as directed acyclic graphs (DAGs) of tasks12,263Airpal Web UI for PrestoDB2,502Aerosolve A machine learning

1.8k
OkHttp线程池和连接池
开源

OkHttp线程池和连接池

OkHttp线程池和连接池了解了OkHttp的网络请求流程以及拦截器实现原理,再关注OkHttp中两个重要的:OkHttp的线程池和连接池。1、OkHttp线程池在OkHttp网络请求流程一文中,我们分析了OkHttp异步和同步请求流程。请求最后都在Dispatcher中分发调度处理,最后被ExecutorService执行。1.1、DispatcherDispatcher中执行任务的执行器是ex

5.2k
优美的开源动效库:Lottie
开源

优美的开源动效库:Lottie

优美的开源动效库:Lottie1、强大的动效LottieLottie是一个适用于Android,iOS,Web和Windows的库,它可以使用Bodymovin解析以json格式导出的Adobe After Effects动画,并在移动设备和Web上原生渲染它们!GitHub:https://github.com/airbnb/lottie-androidLottie官网:http://airbn

4.3k