Android設(shè)計模式之Builder模式

前言 : Android中的AlertDialog用的就是builder設(shè)計模式砰蠢,圖片加載控件Universal-Image-Loader的初始化配置也是使用的builder設(shè)計模式系洛,之后接觸的Glide以及Picasso都是使用了Builder模式顶吮,膜拜。此外髓介,經(jīng)典的網(wǎng)絡(luò)架構(gòu)十籍,okhttp中也使用了Builder模式,所以平時我們還是接觸不少創(chuàng)建型模式杀糯。(如果我們想要使用的控件或者是對象時,不是new出來的苍苞,而是build出來的固翰,多半使用的就是Builder設(shè)計模式)那么他們?yōu)槭裁词褂胋uilder設(shè)計模式,什么情況下我們應(yīng)該考慮使用builder設(shè)計模式呢?我看了幾篇關(guān)于Builder設(shè)計模式的文章,其中有三篇都是定義然后介紹一下模式的有點還有缺點,以及使用場景,說實話,我看完之后是懵逼的.有一篇寫的還不錯,本文也主要是借鑒,文章末尾,我會給出出處.

模式的定義

將一個復(fù)雜對象的構(gòu)建與它的表示分離羹呵,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示骂际。

UML圖

UML圖

角色介紹

Product 產(chǎn)品類 : 產(chǎn)品的抽象類。

Builder : 抽象類冈欢, 規(guī)范產(chǎn)品的組建歉铝,一般是由子類實現(xiàn)具體的組件過程。

ConcreteBuilder : 具體的構(gòu)建器.

Director : 統(tǒng)一組裝過程(可省略)凑耻。

應(yīng)用場景: 復(fù)雜對象的創(chuàng)建,內(nèi)部包含多個部件或者零件

優(yōu)點:使用靈活犯戏,易于擴展;不用關(guān)心內(nèi)部實現(xiàn)細節(jié)拳话,只注重結(jié)果

缺點:產(chǎn)生多余的Builder對象,消耗內(nèi)存种吸。

下面舉例來說明一下,畢竟例子是比較好的體現(xiàn)方式.

如果我們有一個類Student弃衍,他有很多的屬性,但是僅僅姓名和學號是必須賦值的坚俗,其他的屬性都是可選項镜盯,比如像下面代碼中所示.

public class Student {

private final int stuId;//必須

private final String name;//必須

private final int age;//可選

private final int gender;//可選

private final int address;//可選

...//還有很多可選屬性

}

那么我們怎么來創(chuàng)建一個Student對象呢岸裙?我們看到每個屬性都用final來修飾了,說明每個屬性都要在構(gòu)造方法中被初始化速缆,我們又必須提供各種參數(shù)數(shù)量的構(gòu)造方法降允,我們看如下代碼

public class Student {

private final int stuId;//必須

private final String name;//必須

private final int age;//可選

private final int gender;//可選

private final String address;//可選

public Student(int stuId,String name){

this(stuId,name,0,1,"");

}

public Student(int stuId,String name,int age){

this(stuId,name,age,1,"");

}

public Student(int stuId,String name,int age,int gender){

this(stuId,name,age,gender,"");

}

public Student(int stuId,String name,int age,int gender,String address){

this.stuId = stuId;

this.name = name;

this.age = age;

this.gender = gender;

this.address = address;

}

}

這樣做確實可以解決我們的需求,但這還是可選參數(shù)不多的情況艺糜,如果有很多可選參數(shù)剧董,我們就必須要寫很多個構(gòu)造函數(shù),這將導(dǎo)致代碼的可讀性和維護性變差破停,更重要的是翅楼,當我們要用到這個類的時候會感覺無從下手,我到底應(yīng)該用哪個構(gòu)造方法呢真慢?應(yīng)該用兩個參數(shù)的構(gòu)造方法還是用三個參數(shù)的呢毅臊?如果我用兩個參數(shù)的構(gòu)造方法,那么可選參數(shù)的默認值是多少黑界?

更棘手的是管嬉,如果我只想給Student對象設(shè)置address屬性而不設(shè)置age和gender屬性的話怎么辦?我們顯然還得再繼續(xù)添加構(gòu)造方法朗鸠,或者我們只能調(diào)用全參的構(gòu)造方法蚯撩,然后給age和gender屬性設(shè)置個默認值。

還有一點童社,我們看到stuId求厕,age,gender都是int類型的扰楼,那么我們在創(chuàng)建Student對象時呀癣,哪一個int類型的對象代表stuId,哪一個代表age弦赖,這還進一步增加了使用成本项栏。

那么我們還有沒有其他的辦法?答案是有蹬竖!我們可以只設(shè)置一個默認的無參構(gòu)造方法沼沈,然后給每個屬性添加getter和setter方法,代碼如下

public class Student {

private int stuId;//必須

private String name;//必須

private int age;//可選

private int gender;//可選

private String address;//可選

public Student(){

}

public int getStuId() {

return stuId;

}

public void setStuId(int stuId) {

this.stuId = stuId;

}

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 getGender() {

return gender;

}

public void setGender(int gender) {

this.gender = gender;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

}

這種方法看上去可讀性和維護性比較好币厕,當我們使用這個類的時候只需要創(chuàng)建一個空的對象并且設(shè)置我們需要的屬性就可以了列另。比如這樣:

Student stu = new Student();

stu.setStuId(1);

stu.setName("小明");

stu.setAge(12);

這樣做有兩個問題,第一個問題是我們的stu對象沒有一個創(chuàng)建完畢的標識旦装,上面的stu對象我們設(shè)置了三個屬性页衙,但當別人看到這段代碼時,他不確定這個stu對象是只需要這三個屬性還是當時作者忘了寫完整,除非所有的屬性都給set上店乐,別人才能確保你這個對象創(chuàng)建完畢艰躺;另一個問題是任何人都可以在我們創(chuàng)建好的基礎(chǔ)上繼續(xù)改變它,也就是繼續(xù)給它set新的屬性或者刪除某個已經(jīng)set的屬性眨八,這就會使我們的stu對象具有可變性腺兴,這會引起潛在的風險。

好在我們還有第三種方法廉侧,那就是builder設(shè)計模式了页响。

public class Student {

private final int stuId;//必須

private final String name;//必須

private final int age;//可選

private final int gender;//可選

private final String address;//可選

private Student(StudentBuilder builder){

this.stuId = builder.stuId;

this.name = builder.name;

this.age = builder.age;

this.gender = builder.gender;

this.address = builder.address;

}

public int getStuId() {

return stuId;

}

public String getName() {

return name;

}

public int getAge() {

return age;

}

public int getGender() {

return gender;

}

public String getAddress() {

return address;

}

public static class StudentBuilder{

private final int stuId;

private final String name;

private int age;

private int gender;

private String address;

public StudentBuilder(int stuId,String name){

this.stuId = stuId;

this.name = name;

}

public StudentBuilder setAge(int age){

this.age = age;

return this;

}

public StudentBuilder setGender(int gender){

this.gender = gender;

return this;

}

public StudentBuilder setAddress(String address){

this.address = address;

return this;

}

public Student build(){

return new Student(this);

}

}

}

值得注意的幾點:

1.Student的構(gòu)造方法是私有的,也就是說我們不能直接new出Student對象

2.我們又將Student的屬性用final修飾了伏穆,并且我們在構(gòu)造方法中都為他們進行了初始化操作拘泞,我們只提供了getter方法

3.使用builder模式構(gòu)造出來的對象有更好的可讀性,等下我們會看到

4.StudentBuilder的屬性中只給我們必須的屬性添加的final修飾枕扫,所以我們必須在StudentBuilder的構(gòu)造方法中為他們初始化

使用builder設(shè)計模式完美的解決了方法一和方法二的不足陪腌,并且兼具他們的優(yōu)點:具有必填屬性和可選屬性的區(qū)分,更重要的是:可讀性很強烟瞧。唯一的不足是我們要在StudentBuilder中重復(fù)的寫一遍Student中的屬性诗鸭。

好,現(xiàn)在我們來創(chuàng)建一個Student對象吧

public Student getStudent(){

return new Student.StudentBuilder(1,"小明")//必填屬性在構(gòu)造方法中賦值

.setAge(1)//設(shè)置可選屬性 年齡

.setGender(1)//設(shè)置可選屬性 性別 默認1為男

.build();//對象構(gòu)建完畢的標識参滴,返回Student對象

}

非常優(yōu)雅有木有强岸?他是一個鏈式的調(diào)用,我們可以1行代碼就搞定砾赔,更重要的是蝌箍,他的可讀性非常強,而且通過build()我們可以很明確的告訴別人我們的Student已經(jīng)創(chuàng)建完畢暴心。

builder設(shè)計模式非常靈活妓盲,一個builder可以創(chuàng)建出各種各樣的對象,我們只需要在build()之前調(diào)用set方法來為我們的對象賦值专普。

builder模式另一個重要特性是:它可以對參數(shù)進行合法性驗證悯衬,如果我們傳入的參數(shù)無效,我們可以拋出一個IllegalStateException異常檀夹,但是我們在哪里進行參數(shù)合法性驗證也是有講究的:那就是在對象創(chuàng)建之后進行合法性驗證筋粗。我們修改StudentBuilder的build()方法

public Student build(){

Student student = new Student(this);

if (student.getAge()>120){

throw? new IllegalStateException("年齡超出限制");

}

return student;

}

為什么要先創(chuàng)建對象,再進行參數(shù)驗證炸渡?因為我們的StudentBuilder是線程不安全的娜亿,如果我們先進行參數(shù)驗證后創(chuàng)建對象,那么創(chuàng)建對象的時候?qū)ο蟮膶傩钥赡芤呀?jīng)被其他線程改變了蚌堵,例如下面的代碼就是錯誤的

/**

* 錯誤的

*/

public Student build(){

if (age>120){

throw? new IllegalStateException("年齡超出限制");

}

return new Student(this);

}

最后總結(jié)一下builder設(shè)計模式:當我們的類中有很多屬性的時候暇唾,更重要的是有很多可選屬性的時候,我們就可以使用builder設(shè)計模式,因為這樣不僅可以使我們的類使用起來很優(yōu)雅策州,而且還可以給我們的對象一個創(chuàng)建完成的標識,即build()方法宫仗。此種多是鏈式調(diào)用够挂,用起來方便,賞心悅目藕夫。

另外孽糖,可以看下AlertDialog的源碼,這里我就不貼出來了毅贮,太多办悟。

本文來源: http://blog.csdn.net/nugongahou110/article/details/50395698

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市滩褥,隨后出現(xiàn)的幾起案子病蛉,更是在濱河造成了極大的恐慌,老刑警劉巖瑰煎,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铺然,死亡現(xiàn)場離奇詭異,居然都是意外死亡酒甸,警方通過查閱死者的電腦和手機魄健,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來插勤,“玉大人沽瘦,你說我怎么就攤上這事∨┘猓” “怎么了析恋?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卤橄。 經(jīng)常有香客問我绿满,道長,這世上最難降的妖魔是什么窟扑? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任喇颁,我火速辦了婚禮,結(jié)果婚禮上嚎货,老公的妹妹穿的比我還像新娘橘霎。我一直安慰自己,他們只是感情好殖属,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布姐叁。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪外潜。 梳的紋絲不亂的頭發(fā)上原环,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音处窥,去河邊找鬼嘱吗。 笑死,一個胖子當著我的面吹牛滔驾,可吹牛的內(nèi)容都是我干的谒麦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼哆致,長吁一口氣:“原來是場噩夢啊……” “哼绕德!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摊阀,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤耻蛇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驹溃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體城丧,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年豌鹤,在試婚紗的時候發(fā)現(xiàn)自己被綠了亡哄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡布疙,死狀恐怖蚊惯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灵临,我是刑警寧澤截型,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站儒溉,受9級特大地震影響宦焦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顿涣,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一波闹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涛碑,春花似錦精堕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘫证。三九已至,卻和暖如春庄撮,著一層夾襖步出監(jiān)牢的瞬間背捌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工洞斯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留载萌,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓巡扇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親垮衷。 傳聞我的和親對象是個殘疾皇子厅翔,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)搀突,斷路器刀闷,智...
    卡卡羅2017閱讀 134,661評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法仰迁,內(nèi)部類的語法甸昏,繼承相關(guān)的語法,異常的語法徐许,線程的語...
    子非魚_t_閱讀 31,639評論 18 399
  • 一施蜜、定義 講一個復(fù)雜對象的構(gòu)建與他的表示分離,使用同樣的構(gòu)建過程實現(xiàn)不同的表示雌隅; 分類:創(chuàng)建型模式 二翻默、使用場景 ...
    夢語少年閱讀 424評論 2 2
  • 那次去西安修械,也許是不一樣的心情,讓我聽到了不一樣的東西检盼,由此產(chǎn)生了一種沖動肯污,去寫點什么的沖動,也因此列了提綱吨枉,準備...
    風不千山閱讀 564評論 0 1
  • 習慣的形成絕不是一朝一夕培養(yǎng)出來的,所以想要改變也是很難的属提。 但是損人不利己的壞習慣一定要清除权逗,把優(yōu)秀的選擇方...
    Fineyoga岳喜榮閱讀 179評論 0 0