使用Builder優雅的生成對象

一,產生背景:屬性較多的對象,一直分開設置比較麻煩;于是產生了builder方式生成對象。

1.1,分開設置對象的屬性

如:

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;
}
}

如此定義對象,要new一個就會是這樣:

Student stu = new Student(1,hu,21);
or
Student stu1 = new Student(2,hu,21,1,shenzhen); 
or
(如果你定義了set方法)
Student stu2 = new Student();
stu2.setAge(xxx);
stu2.setGender(1);
...

看上去似乎不是很優雅,但是使用builder就可以鏈式設置對象參數

1.2,builder鏈式設置對象參數

需要你如下定義對象:

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;
    }

    // 自動生成toString方法快捷鍵,鼠標右鍵--》source--》generateToString
    @Override
    public String toString() {
        return "Student [stuId=" + stuId + ", name=" + name + ", age=" + age + ", gender=" + gender + ", address="
                + address + "]";
    }

    public static class StudentBuilder {
        // private final int stuId;//將Student的屬性用final修飾了,就一定需要先初始化
        // 而且,這里我們只提供,get方法獲得屬性值
        // private final String name;//將Student的屬性用final修飾了

        private int stuId;// 將Student的屬性用final修飾了
        private String name;// 將Student的屬性用final修飾了

        private int age;
        private int gender;
        private String address;

        public StudentBuilder() {

        }

        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;
        }

        /**
         * 最后需要一個build()方法,返回這個設置完參數的對象
         * 
         * @return
         */
        public Student build() {
            // 返回新建的這個對象
            return new Student(this);
        }
    }

}

使用時,鏈式設置就行,記得最后來一個build(),構建(返回)這么一個對象:

        Student student1 = new Student.StudentBuilder(1, "小明")// 必填屬性在構造方法中賦值
                .setAge(1)// 設置可選屬性 年齡
                .setGender(1)// 設置可選屬性 性別 默認1為男
                .build();// 對象構建完畢的標識,返回Student對象

        //沒有必設置的值
        Student student2 = new Student.StudentBuilder().setAge(21).setGender(2).build();

二,進階

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

public Student build(){
            Student student = new Student(this);
            if (student.getAge()>120){
                throw  new IllegalStateException("年齡超出限制");
            }
            return student;
        }

為什么要先創建對象,再進行參數驗證?因為我們的StudentBuilder是線程不安全的,如果我們先進行參數驗證后創建對象,那么創建對象的時候對象的屬性可能已經被其他線程改變了,例如下面的代碼就是錯誤的

/**
         * 錯誤的
         */
        public Student build(){
            if (age>120){
                throw  new IllegalStateException("年齡超出限制");
            }
            return new Student(this);
        }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,973評論 19 139
  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,176評論 6 13
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,969評論 6 342
  • 1 場景問題# 1.1 繼續導出數據的應用框架## 在討論工廠方法模式的時候,提到了一個導出數據的應用框架。 對于...
    七寸知架構閱讀 5,875評論 1 64
  • 做個獨立的行者, 永遠不要怪別人不幫你, 也別怪他人不關心你。 活在世上, 我們都是獨立的個體, 痛苦難受都得自己...
    請叫我安然閱讀 278評論 0 0