首頁>技術>

本文基於《Effective Java》一書中的第二條;

前言

一般我們寫引數如果寫個一兩個,那就可以了,如果寫七八個,那就有點難受了。如果寫十幾個?尼瑪,難受,我要去緩緩。

於是乎,一種新的方法策略運用而生。那就是builder模式,在構造方法的引數過多時,可以方便的進行建立一個類物件。所以本文的中心主旨一句話總結:當構造方法的引數過多時,推薦使用builder模式

既然推薦使用builder模式,那我們一個一個來,分析一下如果不使用builder模式有什麼缺點。

一、傳統方式的缺點1、可伸縮構造方法

可伸縮構造方法就是我們平時書寫最常見的那種,請看下文程式碼;

public class Student { private int id; //必要 private String name;//必要 private int age; //可選 private int sclass; //可選 private int height;//可選 private float weight;//可選 private float score;//可選 //建構函式1:預設構造方法 public Student() {}; //構造方法2:必要欄位構造方法 public Student(int id, String name) {  this.id = id;  this.name = name; } //構造方法3:全部欄位構造方法 public Student(int id, String name, int age, int sclass, int height, float weight, float score) {  super();  this.id = id;  this.name = name;  this.age = age;  this.sclass = sclass;  this.height = height;  this.weight = weight;  this.score = score; }}

下面如果我們要建立一個Student類,一般這樣建立,看下面程式碼:

缺點1:反轉欄位,編譯器不會報錯

比如上面的欄位裡面有一個weight和一個score,都是float型別,如果在new一個Student類時,不小心寫反了,編譯器不會察覺。

缺點3:不想設定的引數,卻不得不設定值

有時候我們的Student只想著設定ID、name和age欄位,其他的無關緊要,但是這種模式必須要設定所有的屬性值。

既然上面有這些缺點,我們可能還想到另外一種方式,那就是javaBean。

2、javaBean模式

先看javaBean模式如何寫的。

public class Student { private int id; //必要 private String name;//必要 private int age; //可選 private int sclass; //可選 private int height;//可選 private float weight;//可選 private float score;//可選 //建構函式1:預設構造方法 public Student() {}    //getter和setter方法 public int getId() {return id;} public void setId(int id) {this.id = id;} public String getName() {return name;} public void setName(String name) {this.name = name;} public int getAge() {return age;} public void setAge(int age) {this.age = age;} public int getSclass() {return sclass;} public void setSclass(int sclass) {this.sclass = sclass;} public int getHeight() {return height;} public void setHeight(int height) {this.height = height;} public float getWeight() {return weight;} public void setWeight(float weight) {this.weight = weight;} public float getScore() {return score;} public void setScore(float score) {this.score = score;};}

這種模式,看起來還比較舒服,只是設定了相應的getter和setter方法。再來看看如何使用這種方式去new一個Student類。

缺點1:構造過程中 JavaBean可能處於不一致的狀態

JavaBeans 模式本身有嚴重的缺陷。由於構造方法在多次呼叫中被分割,所以在構造過程中 JavaBean 可能處於不一致的狀態。該類沒有透過檢查構造引數引數的有效性來執行一致性的選項。在不一致的狀態下嘗試使用物件可能會導致與包含 bug 的程式碼大相徑庭的錯誤,因此很難除錯。

說一下我對其的理解,在上面的例子中,我們的student1物件被多次呼叫了set方法,但是可能有時候在用到這個bean時,剩下的setter方法還沒有做完,於是再次呼叫時發現同一個javaBean呈現出了兩種狀態。於是處於一種不一致的狀態。

缺點2:無法保證javaBean的不可變性

使用第一種模式可伸縮構造方法例項化之後不會更改可變性,所有的資料都是確定好了的。也可以保證執行緒安全。但是提供了setter方法,就不能保證了。比如:

既然前面兩種都存在各種各樣的問題。現在我們再來看今天的主題builder模式,

二、builder模式

還是老樣子,我們先看看builder模式長得什麼樣子。再來分析一下他的優缺點。

public class Student { private int id; // 必要 private String name;// 必要 private int age; // 可選 private int sclass; // 可選 private int height;// 可選 private float weight;// 可選 private float score;// 可選 public Student(Builder builder) {  this.id = builder.id;  this.name = builder.name;  this.age = builder.age;  this.sclass = builder.sclass;  this.height = builder.height;  this.weight = builder.weight;  this.score = builder.score; } public static class Builder {  private int id; // 必要  private String name;// 必要  private int age; // 可選  private int sclass; // 可選  private int height;// 可選  private float weight;// 可選  private float score;// 可選  // 必要引數的構造方法  public Builder(int id, String name) {   this.id = id;   this.name = name;  }  public Builder setId(int id) {   this.id = id;   return this;  }  public Builder setName(String name) {   this.name = name;   return this;  }  public Builder setAge(int age) {   this.age = age;   return this;  }  public Builder setSclass(int sclass) {   this.sclass = sclass;   return this;  }  public Builder setHeight(int height) {   this.height = height;   return this;  }  public Builder setWeight(float weight) {   this.weight = weight;   return this;  }  public Builder setScore(float score) {   this.score = score;   return this;  }  // 對外提供的  public Student build() {   return new Student(this);  } }}

上面的程式碼是在內部構造了一個Builder類,然後我們看看如何去使用。

優點1:不存在反轉欄位的情況

上面可以看出,每次新增新欄位值的時候是透過set方式進行的。具有javaBean的優點。

優點2:靈活構造引數

我們把必要的欄位一寫,那些非必要的欄位我們可以自己選擇是不是要set。

優點3:不存在不一致狀態

使用builder模式,物件的建立必須要等到build完成才可以。

優點4:使用靈活

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

缺點:

為了建立物件,首先必須建立它的 builder。雖然建立這個 builder 的成本在實踐中不太可能被注意到,但在效能關鍵的情況下可能會出現問題。而且,builder 模式比伸縮構造方法模式更冗長,因此只有在有足夠的引數時才值得使用它,比如四個或更多。

但是,如果從構造方法或靜態工廠開始,並切換到 builder,當類演化到引數數量失控的時候,過時的構造方法或靜態工廠就會面臨尷尬的處境。因此,所以,最好從一開始就建立一個 builder。

總結

如果我們的引數比較多時,builder模式是一個不錯的選擇,如果比較少時,由於Builder本身也是個物件佔用一定的資源,所以還是使用可伸縮或者是javaBean的那種模式比較好。

11
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 為什麼 MongoDB 選擇B樹,Mysql 選擇B+樹?