本文索引
一、簡介
二、建立 Builder
三、結論
日期:2020 年 8 月 7 日
一、簡介
在物件導向程式設計中,我們很常遇到需要撰寫自己的類別(Class), 而當遇到較為複雜的類別時,在撰寫建構子或再給予初值時,就很常會遇到有些值需要初值有些則不需要 ,又或者是許多的值需要傳入建構子裡,造成程式碼的冗長與難以閱讀。 而這裡就有一個設計模式叫做 建立者模式(Builder Pattern),來解決此問題,有缺點也有優點,我們先看以下的例子:
我們先定義一個個人資料類別:
class Person{
    private final String ID;        //身分字字號
    private String name;            //名稱
    private int gender;             //性別
    private String residentAddress; //戶籍地址
    private String mailingAddress;  //通訊住址
    private String email;           //電子郵件
    private String phone;           //電話
    private String mobilePhone;     //手機

    /** 建構子 1 
     *  @param ID 身分證
     *  @param name 姓名
     *  @param gender 性別 */
    public Person(String ID, String name, int gender){
        this.ID = ID;
        this.name = name;
        this.gender = gender;
    }

    /** 建構子 2
     *  @param ID 身分證
     *  @param name 姓名
     *  @param gender 性別
     *  @param residentAddress 戶籍地址 
     *  @param mailingAddress 通訊地址 
     *  @param email 電子郵件 
     *  @param phone 電話 
     *  @param mobilePhone 手機 */
    public Person(String ID, String name, int gender, String residentAddress, String mailingAddress, String email, String phone, String mobilePhone){
        this(ID, name, gender);
        this.residentAddress = residentAddress;
        this.mailingAddress = mailingAddress;
        this.email = email;
        this.phone = phone;
    }
}

我們可以從上面的定義看出,Person的建構子已經非常的冗長了。 除此之外,我們裡面有許多選項是選填的,例如電話、電子郵件等等,這些都會造成在建構建構子上有困難, 而這時我們就可以利用 Builder Pattern 來重新設計 Student 的建構子。
根據以上的程式,我們會這樣去建構每個人的資料:
Person personA = new Person("xxxxxxxxx", "小明", 0, "台北市XXXXXXXXX", "台北市XXXXXXXXX", "xxxx@gmail.com", "02-xxxxxx", "09xxxxxx" );

Person personB = new Person("xxxxxxxxx", "小王", 0, "高雄市XXXXXXX", "台中市XXXXXXX", null, "02-xxxxxx", null );

Person personC = new Person("xxxxxxxxx", "小美", 1, "新北市XXXXXXXXXXX", "桃園縣XXXXXXXXXXXX", null, null, null );
二、建立 Builder
我們先來看建立者類別的寫法:
class PersonBuilder{
    private String ID;              //身分字字號
    private String name;            //名稱
    private int gender;             //性別
    private String residentAddress; //戶籍地址
    private String mailingAddress;  //通訊住址
    private String email;           //電子郵件
    private String phone;           //電話
    private String mobilePhone;     //手機
    
    /** 建構子 1 
        *  @param ID 身分證
        *  @param name 姓名 */
    public PersonBuilder(String ID, String name){
        this.ID = ID;
        this.name = name;
    }
    /** 設定電話 */
    public PersonBuilder setPhone( String phone ){
        this.phone = phone;
        return this;
    }
    /** 性別 */
    public PersonBuilder setGender( int gender ){
        this.gender = gender;
        return this;
    }
    /** 設定電子郵件 */
    public PersonBuilder setEmail( String email ){
        this.email = email;
        return this;
    }
    /** 設定戶籍地址 */
    public PersonBuilder setResidentAddress( String residentAddress ){
        this.residentAddress = residentAddress;
        return this;
    }
    /** 設定通訊地址 */
    public PersonBuilder setMailingAddress( String mailingAddress ){
        this.mailingAddress = mailingAddress;
        return this;
    }
    /** 與戶籍同地址 */
    public PersonBuilder setSameAddress(){
        this.mailingAddress = residentAddress;
        return this;
    }
    /** 設定手機 */
    public PersonBuilder setMobilePhone( String mobilePhone ){
        this.mobilePhone = mobilePhone;
        return this;
    }
    /** 建立 Person 物件 */
    public Person build(){
        return new Person(ID, name, gender, residentAddress, mailingAddress, email, phone, mobilePhone);
    }
}
建立 Person 物件:
Person personA = new PersonBuilder("xxxxxxxx", "小明")
                            .setGender( 0 )
                            .setResidentAddress( "台北市XXXXXXXXX" )
                            .setSameAddress()
                            .setEmail( "xxxx@gmail.com")
                            .setPhone( "02-xxxxxx" )
                            .setMobilePhone( "09xxxxxx" )
                            .build();

Person personC = new PersonBuilder("xxxxxxxx", "小美")
                            .setGender( 1 )
                            .setResidentAddress( "台北市XXXXXXXXX" )
                            .setMailingAddress( "桃園縣XXXXXXXXXXXX" )
                            .build();   
以這樣直接看起來,是否更容易閱讀了,能兼具程式的可讀性與彈性 ,這個就是建立者模式的精隨所在。
三、結論
根據以上的程式範例,我們可以發現 建立者模式(Builder pattern) 的好處, 但是事實上也是有壞處的,我們可以分為以下幾點

好處:
  • 可讀性高
  • 具有彈性
  • 也能在 Setter 裡設定參數檢查
  • 能留 final 修飾詞在原本的 class 裡
缺點:
  • 程式碼變的冗長
  • 原本的 class 新增參數時,builder 也要做對應的新增
而在 Java 中的有些 Class 也有用到這種設計模式,例如: 我們在撰寫程式時,從簡單到複雜,而在這之中,當整體的類別變得複雜且龐大時,假如自己在使用或建構物件時 ,時常參數定義錯誤或不完全時,就可以去試試看這個方法,雖然會犧牲一些效率與程式碼空間 ,但是要創造出容易維護且彈性高的軟體這些代價是可以被接受的。畢竟我想大家在除錯時 ,一定非常不喜歡看到一大坨長長的程式碼吧!當自己在填入參數時,早就看得眼花撩亂了,更何況是在除錯的環境下!