聲明:原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處。http://www.itdecent.cn/p/afffb69232ac
設(shè)計(jì)模式系列:
30分鐘學(xué)透設(shè)計(jì)模式1-單例模式的前世今生
30分鐘學(xué)透設(shè)計(jì)模式2-隨處可見(jiàn)的Builder模式
30分鐘學(xué)透設(shè)計(jì)模式3-使用最多的Iterator模式
30分鐘學(xué)透設(shè)計(jì)模式4-最簡(jiǎn)單的面向接口編程-簡(jiǎn)單工廠模式
30分鐘學(xué)透設(shè)計(jì)模式5-從代理模式到AOP
一、概述
Builder模式:主要用于復(fù)雜對(duì)象的構(gòu)建,通過(guò)使用該模式可以有效地減少構(gòu)造函數(shù)或方法傳入的參數(shù)數(shù)量。
這對(duì)于有很多配置項(xiàng)的來(lái)初始化的對(duì)象非常適用。
通俗的講,builder模式是一步步地創(chuàng)建一個(gè)超級(jí)復(fù)雜的對(duì)象。
它允許用戶僅通過(guò)復(fù)雜對(duì)象的類型和內(nèi)容就可以構(gòu)建,完全不需要知道其內(nèi)部細(xì)節(jié)。
總之,builder模式,不僅可以讓構(gòu)造函數(shù)瘦身,對(duì)非構(gòu)造方法同樣適用。

二、萬(wàn)年不變的學(xué)生信息管理
上面的概念看起來(lái)還是一頭霧水,不知道這個(gè)builder模式到底有什么用。
那我們通過(guò)一個(gè)學(xué)生信息管理的例子逐步來(lái)說(shuō)明builder模式的使用方法。
1、起源
有這樣一個(gè)類,記錄了學(xué)生信息。其中學(xué)號(hào)、姓名、性別和年齡為必須項(xiàng),其他為非必須項(xiàng)。
public class Student {
private int id; // 學(xué)號(hào)
private String name; // 姓名
private boolean sex; // 性別
private int age; // 年齡
// 非必須項(xiàng)
private String addr; // 家庭地址
private String phone; // 電話
private String email; // 郵件
}
我們想要構(gòu)造這樣一個(gè)類的實(shí)例,首先想到的是這樣去做。
public Student(int id, String name, boolean sex, int age,
String addr, String phone, String email) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.addr = addr;
this.phone = phone;
this.email = email;
}
假設(shè)有個(gè)學(xué)生小王,上面7項(xiàng)信息都有,可以這樣使用。
接下來(lái),學(xué)生小李,只有4項(xiàng)基本信息,其他項(xiàng)都不知道,那我們?cè)趺慈プ觯?br>
很簡(jiǎn)單...
Student student = new Student(1, "Li", true, 18,"", "", "");
這樣讓我們很難區(qū)分出后面三項(xiàng)參數(shù)的具體含義。
當(dāng)然,我們有更高級(jí)的方法去做,例如重載構(gòu)造函數(shù):
public Student(int id, String name, boolean sex, int age) {
this(id, name, sex, age, "");
}
public Student(int id, String name, boolean sex, int age, String addr) {
this(id, name, sex, age, addr, "", "");
}
這里我們可以重載多個(gè)構(gòu)造函數(shù),第一個(gè)構(gòu)造4個(gè)參數(shù),第二個(gè)構(gòu)造5個(gè)參數(shù),以此類推,最終包含所有的參數(shù)。
額,這未免過(guò)于繁瑣,而且也不利于閱讀。
2、使用JavaBeans settter進(jìn)行優(yōu)化
使用setter優(yōu)化話,大概是下面這個(gè)樣子,這也是我們經(jīng)??吹胶褪褂玫摹?/p>
Student student = new Student();
student.setId(1);
student.setName("Li");
student.setSex(true);
student.setAge(18);
student.setAddr("BeiJing");
student.setPhone("13888888888");
同樣地,使用setter方法會(huì)產(chǎn)生大量重復(fù)的代碼,而且代碼外觀看起來(lái)不是很優(yōu)雅。
并且,JavaBeans模式有著嚴(yán)重的缺點(diǎn),構(gòu)造過(guò)程分散到多個(gè)setter方法中,構(gòu)造過(guò)程中由于多線程操作,JavaBean可能處于不一致?tīng)顟B(tài)。
3、使用優(yōu)雅地Builder模式
先看下結(jié)果:
Student student = new Student.Builder()
.setId(1)
.isFemale(false)
.setName("Li")
.setAge(18)
.build();
這種形式的代碼是不是非常熟悉呢?
比如這樣:
String str = new StringBuilder()
.append("ab")
.reverse()
.toString();
再比如這樣:
URI uri = new URIBuilder("http://www.baidu.com")
.setPath("/hello")
.setParameter("user", "Li")
.setParameter("pwd", "123456")
.build();
// custom() 實(shí)際上返回一個(gè) HttpClientBuilder
HttpClient client = HttpClients.custom().build();
System.out.println(client.execute(new HttpGet(uri)));
是不是非常熟悉呢?尤其是上面發(fā)送Http請(qǐng)求這個(gè)例子,構(gòu)造Client和URI都是使用的Builder模式。
那這種代碼是怎么實(shí)現(xiàn)的呢?
4、動(dòng)手實(shí)現(xiàn)一個(gè)Builder
在Student類中增加靜態(tài)內(nèi)部類:
public static class Builder {
private int id;
private String name;
private boolean sex;
private int age;
private String addr;
private String phone;
private String email;
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder isFemale(boolean sex) {
this.sex = sex;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
// ... 篇幅有限,省略其他的settter
public Student build() {
return new Student(this.id, this.name, this.sex, this.age,
this.addr, this.phone, this.email);
}
}
在客戶端調(diào)用時(shí):
Student student = new Student.Builder()
.setId(1)
.isFemale(false)
.setName("Li")
.setAge(18)
.build();
是不是非常簡(jiǎn)單呢?那custom()那種的是什么情況?
假設(shè)我們有個(gè)Class類來(lái)管理學(xué)生,當(dāng)然學(xué)生這個(gè)場(chǎng)景不能很好的描述問(wèn)題:
public class Class {
public static Student.Builder custom() {
return new Student.Builder();
}
}
那我們實(shí)際調(diào)用的就會(huì)是:
Student student = Class.custom()
.setId(1)
.setName("Li")
.isFemale(false)
.setAge(18)
.build();
上面的HttpClients中,實(shí)際有多個(gè)生成Client的方式,如默認(rèn)的,最小的等等。custom()只是生成一種通用的而已。
三、優(yōu)缺點(diǎn)以及常見(jiàn)使用場(chǎng)景
1、缺點(diǎn)
- 使用builder模式,會(huì)額外的增加代碼(多寫一遍屬性和其setter方法),但對(duì)于客戶端來(lái)講,代碼可讀性會(huì)大大增強(qiáng)。
- 由于builder會(huì)多寫一遍setter方法,在屬性非常多的時(shí)候,開發(fā)者可能會(huì)漏掉一兩個(gè)。
2、缺點(diǎn)
- 使用builder模式, 客戶端無(wú)需了解產(chǎn)品內(nèi)部組成的細(xì)節(jié),將產(chǎn)品本身與產(chǎn)品的創(chuàng)建過(guò)程解耦,使得相同的創(chuàng)建過(guò)程可以創(chuàng)建不同的產(chǎn)品對(duì)象。
- 每一個(gè)具體建造者都相對(duì)獨(dú)立,而與其他的具體建造者無(wú)關(guān),程序更便于擴(kuò)展。
3、場(chǎng)景
如果構(gòu)建對(duì)象時(shí),屬性方法太多,可以試一試builder模式。