你在寫代碼的過程中遇到過構造方法的參數太多、構造方法重載太多,而不知道使用哪個方法創建對象的問題嗎?或者參數傳着傳着就傳錯位置了?
比如 Person 類,包含 id、姓名、性別、身高、體重屬性。爲了方便創建對象,我們一般使用所有屬性作爲參數提供一個最全的構造方法,然後按需提供部分屬性的若干構造方法。代碼如下:
package constxiong.interview.design;
/**
* 對象人
* @author ConstXiong
*/
public class Person {
/**
* id
*/
private final int id;
/**
* 姓名
*/
private final String name;
/**
* 性別
*/
private final String sex;
/**
* 身高
*/
private final Double height;
/**
* 體重
*/
private final Double weight;
public Person(int id, String name) {
this(id, name, null);
}
public Person(int id, String name, String sex) {
this(id, name, sex, null);
}
public Person(int id, String name, String sex, Double height) {
this(id, name, sex, height, null);
}
public Person(int id, String name, String sex, Double height, Double weight) {
this.id = id;
this.name = name;
this.sex = sex;
this.height = height;
this.weight = weight;
}
}
當我們需要創建一個只知道 id、姓名、性別的對象時,調用第 2 個構造方法即可
Person person = new Person(1, "ConstXiong", "男");
這樣做存在問題:
示例中才 5 個參數,實際開發中隨着參數的增多,創建對象會越來越困難,需要確定參數的數量與構造方法中的位置;代碼可讀性很差;容易參數顛倒且很難排查。
經常見到的會有第 2 種做法,提供默認構造方法與屬性的 set 方法。
package constxiong.interview.design;
/**
* 對象人
* @author ConstXiong
*/
public class Person {
/**
* id
*/
private int id;
/**
* 姓名
*/
private String name;
/**
* 性別
*/
private String sex;
/**
* 身高
*/
private Double height;
/**
* 體重
*/
private Double weight;
public Person() {
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setHeight(Double height) {
this.height = height;
}
public void setWeight(Double weight) {
this.weight = weight;
}
}
這種的方式 Person 對象可以這樣創建
Person person = new Person();
person.setId(1);
person.setName("ConstXiong");
person.setSex("男");
person.setHeight(1.70);
person.setWeight(150.0);
這樣做解決了構造方法多個參數和可讀性的問題,但是引入了新的問題,就是無法做到屬性參數在構造完之後是不可改變的,導致存在數據不安全的問題。
第 3 種方式,使用 Builder 模式。代碼如下
package constxiong.interview.design;
/**
* 對象人
* @author ConstXiong
*/
public class Person {
/**
* id
*/
private final int id;
/**
* 姓名
*/
private final String name;
/**
* 性別
*/
private final String sex;
/**
* 身高
*/
private final Double height;
/**
* 體重
*/
private final Double weight;
public static class Builder {
private int id;
private String name;
private String sex;
private Double height;
private Double weight;
public Builder() {
}
public Builder id(int id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder sex(String sex) {
this.sex = sex;
return this;
}
public Builder height(Double height) {
this.height = height;
return this;
}
public Builder weight(Double weight) {
this.weight = weight;
return this;
}
public Person build() {
return new Person(this);
}
}
private Person(Builder builder) {
this.id = builder.id;
this.name = builder.name;
this.sex = builder.sex;
this.height = builder.height;
this.weight = builder.weight;
}
}
創建 Person 對象的代碼
Person person = new Person.Builder()
.id(1)
.name("ConstXiong")
.sex("男")
.height(1.70)
.weight(150.0)
.build();
Builder 模式需要注意是,Builder 類是靜態內部類、類的構造方法是 private 的且參數爲 Builder 對象。
Builder 模式不僅可以解決構造過程數據安全、參數過多、可讀性的問題,還可以自動填充參數、爲生成對象前對參數之間的關係進行合法校驗等...
當然 Builder 模式也帶了新的問題:
- 創新對象前,必須創建 Builder 對象,多一些性能開銷,對性能要求極高的場景下慎用。
- Builder 模式跟 1、2 兩種方式比,代碼行數更多,顯得有點囉嗦。
所以說,軟件開發一般沒有完美的解決方法,只有不同場景的最優解決辦法;一種方法能解決某些問題,必然帶來其他問題。
Builder 模式就非常適合使用較多參數構造對象、須保證對象構造後的屬性字段安全的場景。
【Java面試題與答案】整理推薦