1. 程式人生 > 程式設計 >Java 解決構造方法引數過多-builder模式(effect java 學習筆記2)

Java 解決構造方法引數過多-builder模式(effect java 學習筆記2)

一、前景:

  一般情況我們不會遇到這樣的情況,使用靜態工廠方法,或者構造方法就足夠。但是它們也有一個限制就是,它們不能很好的擴充套件到很多可選引數的場景。隨著我們業務的深入,某些java bean 中的引數將會越來越多,我們新增的構造方法也相應的增加。想想一個10個引數的構造方法,我胃痛。

  既然想要用builder模式,我們首先需要知道傳統方法的不足。


二、可伸縮構造方法 VS builder模式

  我們都用程式碼例項來說話

1、可伸縮構造方法

public class Student {
    private final String name; // required
    private final String sex;  // required
    private final int weight;  // optional
    private final int height;  // optional 
    private final int age;     // optional

    public Student(String name,String sex) {
        this(name,sex,0);
    }
    
    public Student(String name,String sex,int w) {
        this(name,w,int w,int h) {
        this(name,h,0);
    }

    public Student(String name,int h,int a) {
        this.name = name;
        this.sex = sex;
        this.weight = w;
        this.height = h;
        this.age = a;
    }
}
複製程式碼

當我們想建立一個Student例項的時候,可以設定所有引數的最短的夠著方法如下:

Student student = new Student("小明","男",50,150,16);

  通常情況下,這個構造方法可能需要許多你不想設定的引數(是不是有點不爽),隨著引數的不斷增加,它很快會失控的。(你也會發瘋的)。首先我們很難讀懂這個方法,其次如果反轉了兩個引數,編譯器是不會報錯的。比如身高和體重填反了,糟心。。。。。

2、JavaBean模式

  當在構造方法中遇到許多可選引數時,另一種選擇是 JavaBeans 模式,在這種模式中,呼叫一個無引數的構造函 數來建立物件,然後呼叫 setter 方法來設定每個必需的引數和可選引數:

public class Student {
    private  String name; // required
    private  String sex;  // required
    private  int weight;  // optional
    private  int height;  // optional
    private  int age;     // optional

    public Student() {}


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

    public void set
Sex(String sex) { this.sex = sex; } public void setWeight(int weight) { this.weight = weight; } public void setHeight(int height) { this.height = height; } public void setAge(int age) { this.age = age; } } 複製程式碼

這種模式沒有伸縮構造方法的缺點。有點冗長,但建立例項容易,並且容易閱讀

        Student student = new Student();
        student.setName("小明");
        student.setSex("男");
        student.setAge(16);
        student.setWeight(50);
        student.setHeight(150);
複製程式碼

  JavaBeans 模式本身有嚴重的缺陷。由於構造方法在多次呼叫中被分割,所以在構造過程中 JavaBean 可能處於不一致的狀態。該類沒有通過檢查構造引數引數的有效性來執行一致性的選項。在不一致的狀態下嘗試使用 物件可能會導致與包含 bug 的程式碼大相徑庭的錯誤,因此很難除錯。一個相關的缺點是,JavaBeans 模式排除了讓類 不可變的可能性,並且需要在程式設計師的部分增加工作以確保執行緒安全

3、builder模式

  builder 模式結合了可伸縮構造方法模式的安全性和JavaBean模式的可讀性。

public class Student {
    private final String name;
    private final String sex;
    private  final int weight;
    private final int height;
    private  final int age;

    private Student(Builder builder) {
        this.name = builder.name;
        this.sex = builder.sex;
        this.weight = builder.weight;
        this.height = builder.height;
        this.age = builder.age;
    }
    
    public static class Builder {
        private final String name; // required
        private final String sex;  // required
        private int weight;  // optional
        private int height;  // optional
        private int age;     // optional

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

        public Builder setWeight(int weight) {
            this.weight = weight;
            return this;
        }

        public Builder setHeight(int height) {
            this.height = height;
            return this;
        }
        
        public Builder setAge(int age) {
            this.age = age;
            return this;
        }
        
        public Student build() {
            return new Student(this);
        }

    }
}
複製程式碼

  我首先來噴一下,程式碼量翻倍了。但是Student 類是不可變的,所有的引數預設值都在一個地方。builder 的 setter 方法返回 builder 本身, 這樣呼叫就可以被連結起來,從而生成一個流暢的 API。例項如下:

Student student = new Student.Builder("小明","男").setWeight(50)
                .setHeight(150).setAge(16).build();
複製程式碼

  程式碼很容易編寫,更重要的是易於閱讀。 Builder 模式模擬 Python 和 Scala 中的命名可選引數。 這裡沒有涉及到有效性的檢查


總結:

  builder 對構造方法的一個微小的優勢是,builder 可以有多個可變引數,因為每個引數都是在它自己的方法中指 定的。

  Builder 模式非常靈活。 單個 builder 可以重複使用來構建多個物件。 builder 的引數可以在構建方法的呼叫之間 進行調整,以改變建立的物件。 builder 可以在建立物件時自動填充一些屬性,例如每次建立物件時增加的序列號。

  Builder 模式也有缺點。為了建立物件,首先必須建立它的 builder。雖然建立這個 builder 的成本在實踐中不太可 能被注意到,但在效能關鍵的情況下可能會出現問題。而且,builder 模式比伸縮構造方法模式更冗長,因此只有在 有足夠的引數時才值得使用它,比如四個或更多。但是請記住,如果希望在將來新增更多的引數。但是,如果從構造 方法或靜態工廠開始,並切換到 builder,當類演化到引數數量失控的時候,過時的構造方法或靜態工廠就會面臨尷 尬的處境。因此,所以,最好從一開始就建立一個 builder。

  總而言之,當設計類的構造方法或靜態工廠的引數超過幾個時,Builder 模式是一個不錯的選擇,特別是如果許 多引數是可選的或相同型別的。客戶端程式碼比使用伸縮構造方法(telescoping constructors)更容易讀寫,並且 builder 比 JavaBeans 更安全。

ps:如果有微信讀書的書友,可以來微信讀書群傳送門找組織。