Builder模式是一种设计模式,Android源码中AlertDialog就是使用Build设计模式,这种模式的主要特点就是链式的,方便使用者的调用,使用者无需关心内部如何实现就可以方便调用。
首先了解一下定义:
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
相同的方法,不同的执行顺序,产生不同的事件结果时;
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适;
当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
在建造者模式结构图中包含如下几个角色:
● Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。
● ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
● Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
● Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
优点:
缺点:
变种Builder模式在现实Android开发中会经常用到。现实开发中,Director角色会经常被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的关键点是每个setter方法都返回自身,即return this
。调用如下:
new TestBuilder().setA("A").setB("B").create()
好处:
目的在于减少对象创建过程中引入的多个重载构造函数、可选参数以及setter过度使用导致不必要的复杂性。
运用实例可以在最下文的参考资料3中查看关于User对象属性的例子,此处不在赘述。
再举一例:
参加面试的同学可能会有被问到String
和StringBuffer
和StringBuilder
的区别,这里就有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 = ArrayList()
private var networkInterceptors: ArrayList = 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()
private val commOnParams= ArrayMap()
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
)
}
}
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(R.id.md_button_positive)
val negativeButton = findViewById(R.id.md_button_negative)
val neutralButton = findViewById(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模式 详解及学习使用