??Builder設(shè)計模式在代碼設(shè)計中很常見牙寞,譬如我們在閱讀大神們的源碼是扫步,一些初始化參數(shù)比較多構(gòu)造方法都采用了Builder模式不狮,譬如說Okhttp
,Glide
,Picasso
等
Builder模式長什么樣?
??舉個簡單的例子色冀,現(xiàn)在大多數(shù)Android應(yīng)用的開發(fā)網(wǎng)絡(luò)層大多數(shù)都在使用OkHttp,熟悉的人都知道,在使用Okhttp之前要進(jìn)行一些初始化工作挪略,譬如說超時時間
历帚,緩存策略
等等,下面是個簡單的初始化例子:
public static OkHttpClient get() {
if (okHttpClient == null) {
synchronized (AppHttpClient.class) {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
//cookie保存
.cookieJar(new CookiesManager(new PersistentCookieStore(BaseApplication.getInstance())))
.sslSocketFactory(createSSLSocketFactory())
//支持SSL
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
)
//鏈接15S超時
.connectTimeout(15, TimeUnit.SECONDS)
//Read請求30S超時
.readTimeout(30, TimeUnit.SECONDS)
//Write請求30S超時
.writeTimeout(30, TimeUnit.SECONDS)
//增加Header
.addInterceptor(new HeaderInfoInterceptor())
//build
.build();
}
}
}
return okHttpClient;
}
上面的鏈?zhǔn)秸{(diào)用方法進(jìn)行初始化使用的就是Builder設(shè)計模式杠娱,是不是覺得很方便挽牢?
使用場景
??Builder可以將復(fù)雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現(xiàn)方法可以構(gòu)造出不同表現(xiàn)(屬性)的對象摊求。這句話看起來很拗口禽拔,不好理解,下邊我們舉一個簡單的例子:
我們在實際開發(fā)過程當(dāng)中室叉,時常會遇到這樣一個情況睹栖,需要構(gòu)建一個復(fù)雜(屬性N多)的對象,像這樣嬸兒的:
public class Person {
private String name; //必須
private int age; //必須
private int sex; //必須
private float height;
private String phone;
private String address;
private String email;
}
想要new一個這樣的類的實例茧痕,于是我們就開始擼下邊的代碼野来,通過構(gòu)造函數(shù)的參數(shù)的方式去new一個對象:
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, float height) {
this.name = name;
this.age = age;
this.height = height;
}
public Person(String name, int age, float height, int sex) {
this.name = name;
this.age = age;
this.height = height;
this.sex = sex;
}
public Person(String name, int age, float height, int sex, String phone) {
this.name = name;
this.age = age;
this.height = height;
this.sex = sex;
this.phone = phone;
}
public Person(String name, int age, float height, int sex, String phone, String address) {
this.name = name;
this.age = age;
this.height = height;
this.sex = sex;
this.phone = phone;
this.address = address;
}
public Person(String name, int age, float height, int sex, String phone, String address, String email) {
this.name = name;
this.age = age;
this.height = height;
this.sex = sex;
this.phone = phone;
this.address = address;
this.email = email;
}
或者使用getter和setter的方式去寫一個實現(xiàn)類:
public class Person {
private String name; //必須
private int age; //必須
private int sex; //必須
private float height;
private String phone;
private String address;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
先說說這兩種方式的優(yōu)劣:
第一種在參數(shù)不多的情況下,是比較方便快捷的踪旷,一旦參數(shù)多了曼氛,代碼可讀性大大降低豁辉,并且難以維護(hù),對調(diào)用者來說也造成一定困惑舀患;
第二種可讀性不錯徽级,也易于維護(hù),但是這樣子做對象會產(chǎn)生不一致的狀態(tài)聊浅,當(dāng)你想要傳入全部參數(shù)的時候餐抢,你必需將所有的setXX方法調(diào)用完成之后才行。然而一部分的調(diào)用者看到了這個對象后狗超,以為這個對象已經(jīng)創(chuàng)建完畢,就直接使用了朴下,其實Person對象并沒有創(chuàng)建完成努咐,另外,這個Person對象也是可變的殴胧,不可變類所有好處都不復(fù)存在渗稍。
所以有沒有更好地方式去實現(xiàn)它呢,那就是接下來要理解的Builder模式了团滥。
以下介紹的Builder模式其實是Builder模式的衍生模式竿屹,他與最經(jīng)典Builder模式有區(qū)別,因為現(xiàn)在很少使用經(jīng)典Builder模式灸姊,基本都在使用Builder衍生模式(鏈?zhǔn)秸{(diào)用)拱燃,所以經(jīng)典Builder模式本人也沒怎么研究,有興趣的童鞋可以自行研究下設(shè)計模式之四 --- 建造(Builder)模式
新擼出來的代碼:
public class Person {
private final String name; //必須
private final int age; //必須
private final int sex; //必須
private final float height;
private final String phone;
private final String address;
private final String email;
private Person(PersonBuilder builder){
this.name = builder.name;
this.age = builder.age;
this.sex = builder.sex;
this.height = builder.height;
this.phone = builder.phone;
this.address = builder.address;
this.email = builder.email;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public int getSex() {
return sex;
}
public float getHeight() {
return height;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
public String getEmail() {
return email;
}
public static class PersonBuilder{
private final String name; //必須
private final int age; //必須
private final int sex; //必須
private float height;
private String phone;
private String address;
private String email;
public PersonBuilder(String name, int age, int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public PersonBuilder height(float height){
this.height = height;
return this;
}
public PersonBuilder phone(String phone){
this.phone = phone;
return this;
}
public PersonBuilder address(String address){
this.address = address;
return this;
}
public PersonBuilder email(String email){
this.email = email;
return this;
}
public Person build(){
return new Person(this);
}
}
}
從上面的代碼中我們可以看到力惯,我們在PersonBuilder
類里定義了一份與Person
類一模一樣
的變量碗誉,通過一系列的成員函數(shù)進(jìn)行設(shè)置屬性值,但是返回值都是this
父晶,也就是都是PersonBuilder對象
哮缺,最后提供了一個build
函數(shù)用于創(chuàng)建
Person對象,返回的是Person
對象甲喝,對應(yīng)的構(gòu)造函數(shù)在Person類中進(jìn)行定義尝苇,也就是構(gòu)造函數(shù)的入?yún)?/code>是
PersonBuilder
對象,然后依次對自己的成員變量進(jìn)行賦值埠胖,對應(yīng)的值都是PersonBuilder
對象中的值糠溜。此外PersonBuilder類中的成員函數(shù)返回Builder對象自身的另一個作用就是讓它支持鏈?zhǔn)秸{(diào)用,使代碼可讀性大大增強(qiáng)直撤。
Builder模式需要有幾個注意的點:
- Person類的構(gòu)造方法必須是
私有
的诵冒,調(diào)用者不能直接創(chuàng)建
Person對象 - Person類的屬性是不可變的,所有的屬性都要添加
final
修飾符谊惭,并且在構(gòu)造方法中設(shè)置了值汽馋,并且對外只提供getters
方法侮东。 - PersonBuilder的內(nèi)部類構(gòu)造方法中
只接收必傳的參數(shù)
,并且必傳的參數(shù)使用final
修飾符豹芯。
調(diào)用方式
new Person.PersonBuilder("張三", 18, 1)
.address("北京市XXX")
.email("xxxxx@gmail.com")
.height(175.5f)
.phone("1234556")
.build();
相比起前面通過構(gòu)造函數(shù)和setter/getter方法兩種方式,可讀性更強(qiáng)悄雅。唯一可能存在的問題就是會產(chǎn)生多余的Builder對象,消耗內(nèi)存铁蹈。然而大多數(shù)情況下我們的Builder內(nèi)部類使用的是靜態(tài)修飾的(static)宽闲,所以這個問題也沒多大關(guān)系。
怎么樣Builder設(shè)計模式會使你的代碼看上去高大上一些呢握牧?
Android Studio對Builder模式的支持
AS 對Builder這種常用的設(shè)計模式提供了支持容诬,方便我們的代碼編寫,在Plugins中搜索Builder沿腰,會出現(xiàn)一個InnerBuilder
的插件览徒,
在編寫好類和屬性之后,右鍵generate選擇Builder颂龙,選擇屬性
確定之后就會生成Builder模式下的代碼习蓬,當(dāng)然生成的代碼和我們希望的還是有些不同,只需要進(jìn)行小小的修改就可以了措嵌,還是很方便的躲叼!
總結(jié)
優(yōu)點
- 良好的封裝性, 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)企巢;
- 建造者獨立枫慷,容易擴(kuò)展;
缺點
- 會產(chǎn)生多余的Builder對象浪规,消耗內(nèi)存流礁;
- 對象的構(gòu)建過程暴露。