30分鐘學(xué)透設(shè)計(jì)模式2-隨處可見(jiàn)的Builder模式

聲明:原創(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模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容