详解 - Builder模式

Android设计模式”这个系列主要是对Android项目中的设计模式进行分析总结,学习自《Android 源码设计模式解析与实战》,错误之处烦请指正~


Android设计模式系列文章:

1、详解 - 单例模式
2、详解 - Builder模式


一、 概述

1.1 定义

将一个复杂对象的构建与表示分离开来,使得同样的构建过程可以创建不同的表示。

1.2 使用场景

  • 相同的方法,不同的执行顺序,产生不同的结果

  • 多中属性都可以装配到一个对象中,但是产生的运行结果不同

  • 复杂的产品类,或者产品类结果会随调用顺序发生改变

  • 初始化一个对象特别复杂,如参数特别多,且很多参数都具有默认值时

1.3 分析

Builder模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,从而降低两者耦合度。

优点:

  • 良好的封装性,使用Builder模式可以使客户端不必知道产品内部的组成细节
  • Builder独立,易于扩展

缺点:

  • 会产生冗余的Builder对象以及组装对象,消耗内存

二、 实现

2.1 示例

Builder模式非常易于上手,我们通过分析一个简单的demo来感受Builder模式的魅力。

EventBus 框架中的 EventBusBuilder 就采用 Builder 模式实现,我们来分析Builder模式的实现,EventBus 3.0 源码解析见 EventBus3.源码解析

EventBusBuilderEventBus 框架中的个性化配置类,从类名就可以看出这是一个 Builder 模式,通过Builder 对象来组装个性化设置 EventBus 的各项参数配置,包括 是否通过Log输出异常信息logSubscriberExceptions 等。下面看看 EventBusBuilder 的相关源码。

public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    boolean logSubscriberExceptions = true;//是否通过Log输出异常信息
    boolean logNoSubscriberMessages = true;//是否通过Log输出发送事件无订阅者信息
    boolean sendSubscriberExceptionEvent = true;//是否将内部异常信息通过SubscriberExceptionEvent发送出去
    boolean sendNoSubscriberEvent = true;//是否将无订阅者的时间通过NoSubscriberEvent发送出去
    boolean throwSubscriberException;//是否将内部异常信息通过EventBusException抛出
    boolean eventInheritance = true;//发送事件是否支持调用所有父类及实现的接口
    boolean ignoreGeneratedIndex;//是否忽略生成被观察者订阅的方法(通过反射)
    boolean strictMethodVerification;//是否开启严格的方法验证,(public,只有一个参数,不为static及abstract),非法则均抛出异常
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;//发送事件的线程池
    List<Class<?>> skipMethodVerificationForClasses;//对类忽略方法校验,目前未实现
    List<SubscriberInfoIndex> subscriberInfoIndexes;//通过annotation preprocessor生成的订阅者方法list

    EventBusBuilder() {
    }

    /**
     * 设置 logSubscriberExceptions,默认为true
     * @param logSubscriberExceptions
     * @return
     */
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    /**
     * 设置 logNoSubscriberMessages,默认为 true
     * @param logNoSubscriberMessages
     * @return
     */
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    /**
     * 设置 sendSubscriberExceptionEvent,默认为 true
     * @param sendSubscriberExceptionEvent
     * @return
     */
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    /**
     * 设置 sendNoSubscriberEvent,默认为 true
     * @param sendNoSubscriberEvent
     * @return
     */
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    /**
     * 设置 throwSubscriberException,默认为 false
     * @param throwSubscriberException
     * @return
     */
    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
        this.throwSubscriberException = throwSubscriberException;
        return this;
    }

    /**
     * 设置 eventInheritance,默认为 true
     * @param eventInheritance
     * @return
     */
    public EventBusBuilder eventInheritance(boolean eventInheritance) {
        this.eventInheritance = eventInheritance;
        return this;
    }


    /**
     * 设置 executorService
     * @param executorService
     * @return
     */
    public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    /**
     * 设置 skipMethodVerificationForClasses
     * @param clazz
     * @return
     */
    public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
        if (skipMethodVerificationForClasses == null) {
            skipMethodVerificationForClasses = new ArrayList<>();
        }
        skipMethodVerificationForClasses.add(clazz);
        return this;
    }

    /**
     * 设置 ignoreGeneratedIndex
     * @param ignoreGeneratedIndex
     * @return
     */
    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
        return this;
    }

    /**
     * 设置 strictMethodVerification
     * @param strictMethodVerification
     * @return
     */
    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
        this.strictMethodVerification = strictMethodVerification;
        return this;
    }

    /**
     * 设置 subscriberInfoIndexes
     * @param index
     * @return
     */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

    /**
     * 实现修改 EventBus 默认 EventBusBuilder(EventBus.defaultInstance)
     * @return
     */
    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;
        }
    }

    /**
     * 通过 build 方式实现 EventBus 初始化
     * @return
     */
    public EventBus build() {
        return new EventBus(this);
    }

}

上述代码中,EventBusBuilder 类可以设置 EventBus 中的 * logSubscriberExceptions、logNoSubscriberMessages、sendSubscriberExceptionEvent* 等参数,这些参数统一存储在 EventBusBuilder 中。在调用 EventBusBuilder 类的 build() 函数时会创建 EventBus,并且将各项配置用于到 EventBus 中去。

EventBusBuilder 采用 Builder 模式主要是初始化 EventBus 配置项参数复杂,并且很多参数具有默认值,在使用时可能只需要对部分参数进行修改,故采用 Builder 模式方便修改初始化。

2.2 小结

通过 2.1节 可以看出 Builder 模式使用非常方便,并且对于多参数属性初始化来说,极大的简化了工作。

在项目中合理的加入 Builder 模式吧~

三、小结

Builder 模式在 Android 开发中较为常用,通常作为配置类的构造器将配置的构建和表示分离开来,同时也实现了将配置类从目标类中抽离出来,避免了过多的 setter 方法。

Builder 模式比较常见的实现方式是通过调用链实现,这样使得代码简单易懂。


本文对 Builder模式 的分析到此就结束了,部分内容学习自 《Android源码设计模式 解析与实战》

附:

EventBus3(3.0.0)源码解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,799评论 25 707
  • 本文首发于个人博客:Lam's Blog - 谈谈23种设计模式在Android源码及项目中的应用,文章由Mark...
    格子林ll阅读 4,635评论 1 105
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 人生就只如初见。 ___________________________________严薇 遇见之前,懵懵懂懂的...
    罗嫚阅读 428评论 2 10
  • 这里是安宁世界。 欢迎来聊天。 做了很多年数据信息的工作,谋算人心和金钱,却对文字总是念念不忘。 今天在雍和宫拍下...
    V_A阅读 141评论 0 0