構造方法的參數太多,如何解決?

你在寫代碼的過程中遇到過構造方法的參數太多、構造方法重載太多,而不知道使用哪個方法創建對象的問題嗎?或者參數傳着傳着就傳錯位置了?

 

比如 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面試題與答案】整理推薦

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章