文章目录
- 背景
- 为什么要用?
- 使用场景:
- UML图解:
- Builder模式的优缺点
- 变种Builder模式
- 在实际项目的案例:
- 网络请求时通用参数配置
- MaterialDialog对话框
- 小结
背景
Builder模式是一种设计模式,Android源码中AlertDialog就是使用Build设计模式,这种模式的主要特点就是链式的,方便使用者的调用,使用者无需关心内部如何实现就可以方便调用。
为什么要用?
首先了解一下定义:
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
使用场景:
-
相同的方法,不同的执行顺序,产生不同的事件结果时;
-
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
-
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适;
-
当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
UML图解:
在建造者模式结构图中包含如下几个角色:
● 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()
- 1
好处:
目的在于减少对象创建过程中引入的多个重载构造函数、可选参数以及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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
可知这个append()
将传入的str
通过调用父类的方法加到了该对象字符串的后面,并将该对象返回了,这样就可以连续的调用append()
方法,并且保持对象的唯一性(String
每次都会创建一个新的对象)。
在实际项目的案例:
-
网络请求时通用参数配置
private const val DEFAULT_CONNECT_TIMEOUT = 5_000L
private const val OTHER_TIME_OUT = 5_000L
/**
* 测试环境网络请求配置
*/
fun testConfig() {
val httpConfig = ().baseUrl()
// 打印使用http请求日志
.setLogLevel()
// 设置全局超时时间
.connectTimeoutMillis(DEFAULT_CONNECT_TIMEOUT)
.readTimeoutMillis(OTHER_TIME_OUT)
.writeTimeoutMillis(OTHER_TIME_OUT).build()
(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 =
private val commonHeaders = ArrayMap<String, String>()
private val commonParams = ArrayMap<String, String>()
private var sslParam: SSLParam = ()
private var hostnameVerifier: HostnameVerifier =
fun baseUrl(url: String): {
baseUrl = url
return this
}
fun addInterceptor(interceptor: Interceptor): {
(interceptor)
return this
}
fun addNetworkInterceptor(interceptor: Interceptor): {
(interceptor)
return this
}
/**
* 连接超时时间
* @param millis 单位是毫秒(默认10秒)
*/
fun connectTimeoutMillis(millis: Long): {
if (millis <= 0) {
throw IllegalArgumentException("connect timeout must Greater than 0")
}
defaultConnectTimeout = millis
return this
}
/**
* 读取超时时间
* @param millis 单位是毫秒(默认10秒)
*/
fun readTimeoutMillis(millis: Long): {
if (millis <= 0) {
throw IllegalArgumentException("read timeout must Greater than 0")
}
defaultReadTimeout = millis
return this
}
/**
* 写入超时时间
* @param millis 单位是毫秒(默认10秒)
*/
fun writeTimeoutMillis(millis: Long): {
if (millis <= 0) {
throw IllegalArgumentException("write timeout must Greater than 0")
}
defaultWriteTimeout = millis
return this
}
/**
* 连接失败时是否重新进行网络请求
* @param retryOnConnectionFailure 默认为false
*/
fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean): {
= retryOnConnectionFailure
return this
}
/**
* 是否开启cookie
* @param isUseCookie 默认为false
*/
fun useCookie(isUseCookie: Boolean): {
= isUseCookie
return this
}
/**
* 是否使用缓存
* @param isUseCache 默认为false
*/
fun useCache(isUseCache: Boolean): {
= isUseCache
return this
}
/**
* 设置日志级别,参考[]
* @param level 默认为[]
*/
fun setLogLevel(level: ): {
logLevel = level
return this
}
/**
* 设置通用请求header
* @param key header键
* @param value header值
*/
fun commonHeader(key: String, value: String): {
commonHeaders[key] = value
return this
}
/**
* 设置通用请求参数
* @param key 参数键
* @param value 参数值
*/
fun commonParam(key: String, value: String): {
commonParams[key] = value
return this
}
/**
* 配置ssl
* @param param ssl参数,默认不对证书做任何检查
*/
fun sslSocketFactory(param: SSLParam): {
sslParam = param
return this
}
/**
* 主机名验证
* @param verifier 默认允许所有主机名
*/
fun hostnameVerifier(verifier: HostnameVerifier): {
hostnameVerifier = verifier
return this
}
fun build(): HttpConfig {
return HttpConfig(
baseUrl, interceptors, networkInterceptors, defaultConnectTimeout
, defaultReadTimeout, defaultWriteTimeout, retryOnConnectionFailure, isUseCookie
, isUseCache, logLevel, commonHeaders, commonParams, sslParam, hostnameVerifier
)
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
-
MaterialDialog对话框
private fun showSavePromptDialog() {
MaterialDialog(this)
.message(text ="消息提示标题")
.positiveButton("保存") {
// do something
}
.negativeButton(“取消”) {
// do something
}
.showByCZConfig()
}
/**
*MaterialDialog扩展
*/
fun (): MaterialDialog {
show()
doAsync {
(50L)
uiThread {
val positiveButton = findViewById<AppCompatButton>(.md_button_positive)
val negativeButton = findViewById<AppCompatButton>(.md_button_negative)
val neutralButton = findViewById<AppCompatButton>(.md_button_neutral)
((.common_font_red))
((.common_font_black))
((.common_font_black))
}
}
return this
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
其他:如开源的图片框架ImageLoader就是通过ImageLoaderConfig进行配置等等。
小结
在开发中,当遇到一个类的构造器或者静态工厂中具有多个参数,特别是大多数参数是可选的时候,可以考虑使用Builder模式,避免过多的setter方法。Builder模式比较常见的实现形式是通过调用链实现,这样使得代码更简洁、易懂。
参考资料:
1.设计模式系列——建造者模式-Builder Pattern
2.创建型设计模式之Builder模式
: Builder模式 详解及学习使用
4.变种 Builder 模式:优雅的对象构建方式