一篇文章就徹底弄懂建造者模式(Builder Pattern)

背景

當(dāng)一個(gè)類的內(nèi)部數(shù)據(jù)過(guò)于復(fù)雜的時(shí)候(通常是負(fù)責(zé)持有數(shù)據(jù)的類,比如Config、VO、PO、Entity...),要?jiǎng)?chuàng)建的話可能就需要了解這個(gè)類的內(nèi)部結(jié)構(gòu),還有這些東西是怎么組織裝配等一大坨亂七八糟的東西,這個(gè)時(shí)候就會(huì)增加學(xué)習(xí)成本而且會(huì)很混亂,這個(gè)時(shí)候就想啊想一種什么法子來(lái)管理一下這個(gè)類中的數(shù)據(jù)呢,怎么在創(chuàng)建的時(shí)候讓它按部就班的來(lái),并且代碼可讀性很好別讓我看花了眼啊,我要的東西也能都很好設(shè)置進(jìn)來(lái),這就是Builder模式的應(yīng)用場(chǎng)景,Builder模式可以將一個(gè)類的構(gòu)建和表示進(jìn)行分離。

1.介紹

1.1什么是構(gòu)建者模式

創(chuàng)建者模式又叫建造者模式,是將一個(gè)復(fù)雜的對(duì)象的構(gòu)建與它的表示分離,使
得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。創(chuàng)建者模式隱藏了復(fù)雜對(duì)象的創(chuàng)建過(guò)程,它把復(fù)雜對(duì)象的創(chuàng)建過(guò)程加以抽象,通過(guò)子類繼承或者重載的方式,動(dòng)態(tài)的創(chuàng)建具有復(fù)合屬性的對(duì)象。

1.2適用場(chǎng)景:

  • 隔離復(fù)雜對(duì)象的創(chuàng)建和使用,相同的方法,不同執(zhí)行順序,產(chǎn)生不同事件結(jié)果
  • 多個(gè)部件都可以裝配到一個(gè)對(duì)象中,但產(chǎn)生的運(yùn)行結(jié)果不相同
  • 產(chǎn)品類非常復(fù)雜或者產(chǎn)品類因?yàn)檎{(diào)用順序不同而產(chǎn)生不同作用
  • 初始化一個(gè)對(duì)象時(shí),參數(shù)過(guò)多,或者很多參數(shù)具有默認(rèn)值
  • Builder模式不適合創(chuàng)建差異性很大的產(chǎn)品類
    產(chǎn)品內(nèi)部變化復(fù)雜,會(huì)導(dǎo)致需要定義很多具體建造者類實(shí)現(xiàn)變化,增加項(xiàng)目中類的數(shù)量,增加系統(tǒng)的理解難度和運(yùn)行成本
  • 需要生成的產(chǎn)品對(duì)象有復(fù)雜的內(nèi)部結(jié)構(gòu),這些產(chǎn)品對(duì)象具備共性;

1.3 主要作用

在用戶不知道對(duì)象的建造過(guò)程和細(xì)節(jié)的情況下就可以直接創(chuàng)建復(fù)雜的對(duì)象。

  • 用戶只需要給出指定復(fù)雜對(duì)象的類型和內(nèi)容;
  • 建造者模式負(fù)責(zé)按順序創(chuàng)建復(fù)雜對(duì)象(把內(nèi)部的建造過(guò)程和細(xì)節(jié)隱藏起來(lái))

1.4 解決的問(wèn)題

  • 方便用戶創(chuàng)建復(fù)雜的對(duì)象(不需要知道實(shí)現(xiàn)過(guò)程)
  • 代碼復(fù)用性 & 封裝性(將對(duì)象構(gòu)建過(guò)程和細(xì)節(jié)進(jìn)行封裝 & 復(fù)用)

例子:造汽車 & 買汽車。

  1. 工廠(建造者模式):負(fù)責(zé)制造汽車(組裝過(guò)>程和細(xì)節(jié)在工廠內(nèi))
  2. 汽車購(gòu)買者(用戶):你只需要說(shuō)出你需要的>型號(hào)(對(duì)象的類型和內(nèi)容),然后直接購(gòu)買就可>>以使用了
    (不需要知道汽車是怎么組裝的(車輪、車門(mén)、>發(fā)動(dòng)機(jī)、方向盤(pán)等等))

2. 模式原理

2.1 UML類圖 & 組成

builder.png

2.2模式講解:

  • 指揮者(Director)直接和客戶(Client)進(jìn)行需求溝通;
  • 溝通后指揮者將客戶創(chuàng)建產(chǎn)品的需求劃分為各個(gè)部件的建造請(qǐng)求(Builder);
  • 將各個(gè)部件的建造請(qǐng)求委派到具體的建造者(ConcreteBuilder);
  • 各個(gè)具體建造者負(fù)責(zé)進(jìn)行產(chǎn)品部件的構(gòu)建;
  • 最終構(gòu)建成具體產(chǎn)品(Product)。

3.用 builder 模式創(chuàng)建共享單車為例子,示例代碼:

產(chǎn)品類:


public class Bike { 

    private IFrame frame; 
    private ISeat seat; 
    private ITire tire; 
    
    public IFrame getFrame() { 
        return frame; 
    } 
    public void setFrame(IFrame frame) { 
        this.frame = frame; 
    } 
    public ISeat getSeat() { 
        return seat; 
    } 
    public void setSeat(ISeat seat) { 
        this.seat = seat; 
    } 
    public ITire getTire() { 
        return tire; 
    } 
    public void setTire(ITire tire) { 
        this.tire = tire; 
    } 
} 

Builder 類:

// 抽象 builder 類 
public abstract class Builder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
} 

ConcreteBuilder 類 :

// 具體 builder 類 
public class MobikeBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new AlloyFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new DermisSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new SolidTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

public class OfoBuilder extends Builder{ 
    private Bike mBike = new Bike(); 
    @Override 
    void buildFrame() { 
        mBike.setFrame(new CarbonFrame()); 
    } 
    @Override 
    void buildSeat() { 
        mBike.setSeat(new RubberSeat()); 
    } 
    @Override 
    void buildTire() { 
        mBike.setTire(new InflateTire()); 
    } 
    @Override 
    Bike createBike() { 
        return mBike; 
    } 
} 

指揮者類:

public class Director { 
    private Builder mBuilder = null; 
    public Director(Builder builder) { 
        mBuilder = builder; 
    } 
    public Bike construct() { 
        mBuilder.buildFrame(); 
        mBuilder.buildSeat(); 
        mBuilder.buildTire(); 
        return mBuilder.createBike(); 
    } 
}

客戶端使用:

public class Click { 
    public static void main(String[] args) { 
        showBike(new OfoBuilder()); 
        showBike(new MobikeBuilder()); 
    } 
    private void showBike(Builder builder) {
        Director director = new Director(builder); 
        Bike bike = director.construct(); 
        bike.getFrame().frame(); 
        bike.getSeat().seat(); 
        bike.getTire().tire(); 
    } 
} 

上面示例是 Builder模式的常規(guī)用法,導(dǎo)演類 Director 在 Builder模式中具有很重要的作用,它用于指導(dǎo)具體構(gòu)建者如何構(gòu)建產(chǎn)品,控制調(diào)用先后次序,并向調(diào)用者返回完整的產(chǎn)品類,但是有些情況下需要簡(jiǎn)化系統(tǒng)結(jié)構(gòu),可以把Director和抽象建造者進(jìn)行結(jié)合,示例代碼:

改造后的抽象建造者:

public abstract class NewBuilder { 
    abstract void buildFrame(); 
    abstract void buildSeat(); 
    abstract void buildTire(); 
    abstract Bike createBike(); 
    /** 
    * 把導(dǎo)演類中的construct()方法合并到抽象建造者類中 
    * 
    * @return 具體產(chǎn)品對(duì)象 
    */ 
    public Bike construct() { 
        this.buildFrame(); 
        this.buildSeat(); 
        this.buildTire(); 
        return this.createBike(); 
    } 
} 

這樣做確實(shí)簡(jiǎn)化了系統(tǒng)結(jié)構(gòu),但同時(shí)也加重了抽象建造者類的職責(zé),也不是太符合單一職責(zé)原則,如果construct() 過(guò)于復(fù)雜,建議還是封裝到 Director 中
除了上面的用途外,還有另外一個(gè)常用的使用方式,就是當(dāng)一個(gè)類構(gòu)造器需要傳入很多參數(shù)時(shí),如果創(chuàng)建這個(gè)類的實(shí)例,代碼可讀性會(huì)非常差,而且很容易引入錯(cuò)誤,此時(shí)就可以利用 builder模式進(jìn)行重構(gòu),重構(gòu)前示例代碼:

// 省略 getter 和 setter 方法 
public class Computer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public Computer(String cpu, String screen, String memory, String mainboard) { 
        this.cpu = cpu; 
        this.screen = screen; 
        this.memory = memory; 
        this.mainboard = mainboard; 
    } 
} 
public class NewComputer { 
    private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard; 
    public NewComputer() { 
        throw new RuntimeException(“can’t init”); 
    } 
    private NewComputer(Builder builder) { 
        cpu = builder.cpu; 
        screen = builder.screen; 
        memory = builder.memory; 
        mainboard = builder.mainboard; 
    } 
    public static final class Builder { 
        private String cpu; 
        private String screen; 
        private String memory; 
        private String mainboard; 
        
    public Builder() {} 
    
    public Builder cpu(String val) { 
        cpu = val; 
        return this; 
    } 
    public Builder screen(String val) { 
        screen = val; 
        return this; 
    } 
    public Builder memory(String val) { 
        memory = val; 
        return this; 
    } 
    public Builder mainboard(String val) { 
        mainboard = val; 
        return this; 
    } 
    public NewComputer build() {
        return new  NewComputer(this);} 
    } 
} 

客戶端:


public class Click { 
    public static void main(String[] args) { 
        // 非 Builder 模式 
        Computer computer = new Computer(“cpu”, “screen”, “memory”, “mainboard”); 
        // Builder 模式 
        NewComputer newComputer = new NewComputer.Builder() 
        .cpu(“cpu”) 
        .screen(“screen”) 
        .memory(“memory”) 
        .mainboard(“mainboard”) 
        .build(); 
    } 
} 

上面的示例代碼只是傳入四個(gè)參數(shù),如果參數(shù)是十四個(gè)甚至更多,builder 模式的優(yōu)勢(shì)將會(huì)更加明顯,傳遞參數(shù)更加靈活,代碼具有更高的可讀性.

4.優(yōu)缺點(diǎn)比較

一般的套路:優(yōu)點(diǎn)是比較簡(jiǎn)單,開(kāi)發(fā)效率高,缺點(diǎn)是如果參數(shù)真的很多的話鬼知道每個(gè)對(duì)應(yīng)的是什么意思啊。

Builder模式:優(yōu)點(diǎn)是可以將構(gòu)造器的setter方法名取成類似注釋的方式,這樣我們可以很清晰的知道剛才究竟設(shè)置的什么值,可讀性較高,缺點(diǎn)是比較冗長(zhǎng)。

優(yōu)點(diǎn):

  • 使用建造者模式可以使客戶端不必知道產(chǎn)品內(nèi)部組成的細(xì)節(jié)。
  • 具體的建造者類之間是相互獨(dú)立的,這有利于系統(tǒng)的擴(kuò)展。
  • 具體的建造者相互獨(dú)立,因此可以對(duì)建造的過(guò)程逐步細(xì)化,而不會(huì)對(duì)其他模塊產(chǎn)生任何影響。

缺點(diǎn):

  • 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點(diǎn),其組成部分相似;如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。
  • 如果產(chǎn)品的內(nèi)部變化復(fù)雜,可能會(huì)導(dǎo)致需要定義很多具體建造者類來(lái)實(shí)現(xiàn)這種變化,導(dǎo)致系統(tǒng)變得很龐大。

5.建造者模式與抽象工廠模式的比較:

  • 與抽象工廠模式相比,建造者模式返回一個(gè)組裝好的完整產(chǎn)品,而抽象工廠模式返回一系列相關(guān)的產(chǎn)品,這些產(chǎn)品位于不同的產(chǎn)品等級(jí)結(jié)構(gòu),構(gòu)成了一個(gè)產(chǎn)品族 。
  • 在抽象工廠模式中,客戶端實(shí)例化工廠類,然后調(diào)用工廠方法獲取所需產(chǎn)品對(duì)象,而在建造者模式中,客戶端可以不直接調(diào)用建造者的相關(guān)方法,而是通過(guò)指揮者類來(lái)指導(dǎo)如何生成對(duì)象,包括對(duì)象的組裝過(guò)程和建造步驟,它側(cè)重于一步步構(gòu)造一個(gè)復(fù)雜對(duì)象,返回一個(gè)完整的對(duì)象 。
  • 如果將抽象工廠模式看成汽車配件生產(chǎn)工廠,生產(chǎn)一個(gè)產(chǎn)品族的產(chǎn)品,那么建造者模式就是一個(gè)汽車組裝工廠,通過(guò)對(duì)部件的組裝可以返回一輛完整的汽車

改文章已同步到公眾號(hào),歡迎大家關(guān)注!


大前端圈_small.jpg
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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