Android设计模式之Builder模式在实际项目的运用

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。 https://blog.csdn.net/jun5753/article/details/87099462

背景

Builder模式是一种设计模式,Android源码中AlertDialog就是使用Build设计模式,这种模式的主要特点就是链式的,方便使用者的调用,使用者无需关心内部如何实现就可以方便调用。

为什么要用?

首先了解一下定义:

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。

使用场景:
  • 相同的方法,不同的执行顺序,产生不同的事件结果时;

  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;

  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适;

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

UML图解:

5315387-16848739f754e53b

在建造者模式结构图中包含如下几个角色:

● Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。

● ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

● Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。

● Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

Builder模式的优缺点

优点:

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

缺点:

  • 会产生多余的Builder对象以及Director对象,消耗内存

变种Builder模式

变种Builder模式在现实Android开发中会经常用到。现实开发中,Director角色会经常被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的关键点是每个setter方法都返回自身,即return this。调用如下:

new TestBuilder().setA("A").setB("B").create()

好处:

目的在于减少对象创建过程中引入的多个重载构造函数、可选参数以及setter过度使用导致不必要的复杂性。

运用实例可以在最下文的参考资料3中查看关于User对象属性的例子,此处不在赘述。

再举一例:

参加面试的同学可能会有被问到StringStringBufferStringBuilder的区别,这里就有StringBuilder这个字符串构建器,我们来简单看一下这个构建器的用法吧!

  String sb = new StringBuilder().append("I ")
                .append("am ")
                .append("student.").toString();

	//我们看一下这里用的append()函数的源码
	@Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

可知这个append()将传入的str通过调用父类的方法加到了该对象字符串的后面,并将该对象返回了,这样就可以连续的调用append()方法,并且保持对象的唯一性(String每次都会创建一个新的对象)。

在实际项目的案例:

  • 网络请求时通用参数配置
 	private const val DEFAULT_CONNECT_TIMEOUT = 5_000L
    private const val OTHER_TIME_OUT = 5_000L    

	/**
     * 测试环境网络请求配置
     */
    fun testConfig() {
        val httpConfig = HttpConfig.Builder().baseUrl(CommonApi.apiBaseUrl)
            // 打印使用http请求日志
            .setLogLevel(HttpLoggingInterceptor.Level.BODY)
            // 设置全局超时时间
            .connectTimeoutMillis(DEFAULT_CONNECT_TIMEOUT)
            .readTimeoutMillis(OTHER_TIME_OUT)
            .writeTimeoutMillis(OTHER_TIME_OUT).build()
        HttpUtil.initHttpConfig(httpConfig)
    }

	/**
     * 网络请求配置构建者
     */
    class Builder {
        private var baseUrl = ""
        private var interceptors: ArrayList<Interceptor> = ArrayList()
        private var networkInterceptors: ArrayList<Interceptor> = ArrayList()
        private var defaultConnectTimeout = 10_000L
        private var defaultReadTimeout = 10_000L
        private var defaultWriteTimeout = 10_000L
        private var retryOnConnectionFailure = false
        private var isUseCookie = false
        private var isUseCache = false
        private var logLevel = HttpLoggingInterceptor.Level.NONE
        private val commonHeaders = ArrayMap<String, String>()
        private val commonParams = ArrayMap<String, String>()
        private var sslParam: SSLParam = HttpsUtil.getSslSocketFactory()
        private var hostnameVerifier: HostnameVerifier = HttpsUtil.UnSafeHostnameVerifier

        fun baseUrl(url: String): HttpConfig.Builder {
            baseUrl = url
            return this
        }

        fun addInterceptor(interceptor: Interceptor): HttpConfig.Builder {
            interceptors.add(interceptor)
            return this
        }

        fun addNetworkInterceptor(interceptor: Interceptor): HttpConfig.Builder {
            networkInterceptors.add(interceptor)
            return this
        }

        /**
         * 连接超时时间
         * @param millis 单位是毫秒(默认10秒)
         */
        fun connectTimeoutMillis(millis: Long): HttpConfig.Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("connect timeout must Greater than 0")
            }
            defaultConnectTimeout = millis
            return this
        }

        /**
         * 读取超时时间
         * @param millis 单位是毫秒(默认10秒)
         */
        fun readTimeoutMillis(millis: Long): HttpConfig.Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("read timeout must Greater than 0")
            }
            defaultReadTimeout = millis
            return this
        }

        /**
         * 写入超时时间
         * @param millis 单位是毫秒(默认10秒)
         */
        fun writeTimeoutMillis(millis: Long): HttpConfig.Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("write timeout must Greater than 0")
            }
            defaultWriteTimeout = millis
            return this
        }

        /**
         * 连接失败时是否重新进行网络请求
         * @param retryOnConnectionFailure 默认为false
         */
        fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean): HttpConfig.Builder {
            this.retryOnConnectionFailure = retryOnConnectionFailure
            return this
        }

        /**
         * 是否开启cookie
         * @param isUseCookie 默认为false
         */
        fun useCookie(isUseCookie: Boolean): HttpConfig.Builder {
            this.isUseCookie = isUseCookie
            return this
        }

        /**
         * 是否使用缓存
         * @param isUseCache 默认为false
         */
        fun useCache(isUseCache: Boolean): HttpConfig.Builder {
            this.isUseCache = isUseCache
            return this
        }

        /**
         * 设置日志级别,参考[HttpLoggingInterceptor.Level]
         * @param level 默认为[HttpLoggingInterceptor.Level.NONE]
         */
        fun setLogLevel(level: HttpLoggingInterceptor.Level): HttpConfig.Builder {
            logLevel = level
            return this
        }

        /**
         * 设置通用请求header
         * @param key header键
         * @param value header值
         */
        fun commonHeader(key: String, value: String): HttpConfig.Builder {
            commonHeaders[key] = value
            return this
        }

        /**
         * 设置通用请求参数
         * @param key 参数键
         * @param value 参数值
         */
        fun commonParam(key: String, value: String): HttpConfig.Builder {
            commonParams[key] = value
            return this
        }

        /**
         * 配置ssl
         * @param param ssl参数,默认不对证书做任何检查
         */
        fun sslSocketFactory(param: SSLParam): HttpConfig.Builder {
            sslParam = param
            return this
        }

        /**
         * 主机名验证
         * @param verifier 默认允许所有主机名
         */
        fun hostnameVerifier(verifier: HostnameVerifier): HttpConfig.Builder {
            hostnameVerifier = verifier
            return this
        }

        fun build(): HttpConfig {
            return HttpConfig(
                baseUrl, interceptors, networkInterceptors, defaultConnectTimeout
                , defaultReadTimeout, defaultWriteTimeout, retryOnConnectionFailure, isUseCookie
                , isUseCache, logLevel, commonHeaders, commonParams, sslParam, hostnameVerifier
            )
        }
    }
  • MaterialDialog对话框
   private fun showSavePromptDialog() {
        MaterialDialog(this)
            .message(text ="消息提示标题")
            .positiveButton("保存") {
               // do something
            }
            .negativeButton(“取消”) {
               // do something
            }
            .showByCZConfig()
    }

/**
*MaterialDialog扩展
*/
fun MaterialDialog.showByCZConfig(): MaterialDialog {
    show()
    doAsync {
        SystemClock.sleep(50L)
        uiThread {
         val positiveButton = findViewById<AppCompatButton>(R.id.md_button_positive)
         val negativeButton = findViewById<AppCompatButton>(R.id.md_button_negative)
         val neutralButton = findViewById<AppCompatButton>(R.id.md_button_neutral)

         positiveButton.setTextColor(ResourcesUtil.getColor(R.color.common_font_red))
         negativeButton.setTextColor(ResourcesUtil.getColor(R.color.common_font_black))
         neutralButton.setTextColor(ResourcesUtil.getColor(R.color.common_font_black))
        }
    }
    return this
}

其他:如开源的图片框架ImageLoader就是通过ImageLoaderConfig进行配置等等。

小结

在开发中,当遇到一个类的构造器或者静态工厂中具有多个参数,特别是大多数参数是可选的时候,可以考虑使用Builder模式,避免过多的setter方法。Builder模式比较常见的实现形式是通过调用链实现,这样使得代码更简洁、易懂。

参考资料:

1.设计模式系列——建造者模式-Builder Pattern

2.创建型设计模式之Builder模式

3.Android : Builder模式 详解及学习使用

4.变种 Builder 模式:优雅的对象构建方式

猜你喜欢

转载自blog.csdn.net/jun5753/article/details/87099462