優(yōu)雅編程之builder模式

問題場景

有如下一個Person類,包含姓名、年齡、住址、電話字段,創(chuàng)建Person類時,要對屬性賦值,并且在對象創(chuàng)建后不允許在更改了。

public class Person{
//    姓名
    private String name;
//    年齡
    private Integer age;
//    家庭住址
    private String address;
//    手機號碼
    private Integer phone;
    
    // setter getter constructor 省略
    // ....
}

解決方案

1. constructor 構(gòu)造

  • 使用全參構(gòu)造器 ,實參傳入會大量包含null,代碼丑陋不優(yōu)雅
    • 創(chuàng)建只有name屬性的對象
      new Person("zhang",null,null,null);
    • 創(chuàng)建有name和phone
      new Person("zhang",null,null,10086);
  • 構(gòu)造器重載
    構(gòu)造器重載能有效避免實參出現(xiàn)大量null,但可想而知,類的屬性多了以后,會出現(xiàn)很多的構(gòu)造器。

2.setter注入

Person person = new Person();
person.setName("張三");
person.setAddress("地址");
person.setAge(11);

這樣有兩個問題,

  1. 多次出現(xiàn)person.setXXX,代碼丑陋。
  2. 不能保證屬性一定是在創(chuàng)建時賦值。

3.setter升級版 鏈式setter

傳統(tǒng)set方法返回void,可以稍作修改,直接返回Person

    public Person setName(String name){
        this.name = name;
        return this;
    }

    public Person setAge(Integer age){
        this.age = age;
        return this;
    }

    public Person setAddress(String address){
        this.address = address;
        return this;
    }

    public Person setPhone(Integer phone){
        this.phone = phone;
        return this;
    }

此時代碼就簡化為

Person person = new Person()
                .setName("張三")
                .setAddress("地址")
                .setAge(11);

set鏈式調(diào)用解決了重復(fù)出現(xiàn)person.set的問題,但是不能保證對象屬性只在初始化時賦值。

4.builder 模式

根據(jù)setter鏈式調(diào)用的思路,演進出另一套解決方案

  1. 創(chuàng)建Person全參構(gòu)造器
  2. 新建一個Builder類
public static class Builder{
        //    姓名
        private String name;
        //    年齡
        private Integer age;
        //    家庭住址
        private String address;
        //    手機號碼
        private Integer phone;

        public Builder name(String name){
            this.name = name;
            return this;
        }

        public Builder age(Integer age){
            this.age = age;
            return this;
        }

        public Builder address(String address){
            this.address = address;
            return this;
        }

        public Builder phone(Integer phone){
            this.phone = phone;
            return this;
        }
        public Person toBuilder(){
            return new Person(name,age,address,phone);
        }
    }
  1. 在Person類提供一個靜態(tài)方法返回Builder實例
    public static Builder builder(){ return new Builder(); }
  2. 使用
    此時的使用代碼如下
        Person person = Person.builder()
                .address("北京")
                .age(11)
                .name("lisi")
                .toBuilder();
優(yōu)點:

簡化客戶端調(diào)用時代碼,保證屬性在實例創(chuàng)建時賦值

缺點:

要編寫B(tài)uilder類,對provider來說代碼量增加,且有大量冗余。

適用場景:
  • 對象有很多屬性需要賦值
  • 在對象初始化后不再需要重新賦值
builder在開源框架中的應(yīng)用:
  1. 在swagger配置ApiInfo時使用提供了builder模式創(chuàng)建,如下:
new ApiInfoBuilder()
                // API 標題
                .title("標題")
                // API描述
                .description("詳情請百度")
                // 版本號
                .version(1.0)
                .license("apache 2.0")
                .build();

根據(jù)名稱,很容易知道,目標類是ApiInfo,構(gòu)造器是ApiInfoBuilder
查看源碼,兩者都包含如下共同字段,符合build模式的基礎(chǔ)

  private final String version;
  private final String title;
  private final String description;
  private final String termsOfServiceUrl;
  private final String license;
  private final String licenseUrl;
  private final Contact contact;
  private List<VendorExtension> vendorExtensions

再查看build方法源碼,就是使用全參構(gòu)造器,創(chuàng)建一個目標實例。

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

相關(guān)閱讀更多精彩內(nèi)容

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