Java設計模式:Builder(構建器)模式


概念定義

Builder模式是一步一步創建一個復雜對象的創建型模式。該模式將構建復雜對象的過程和它的部件解耦,使得構建過程和部件的表示隔離開來。

應用場景

  • 對象創建過程比較復雜,或對創建順序或組合有依賴(經典Builder模式,可參閱GOF《設計模式》)。
  • 創建對象時所需參數較多,且包含較多可選參數(變種Builder模式,可參閱《Effective Java》構建器小節)。

示例代碼

本節側重變種Builder模式,示例代碼如下:

public class RobustPerson {
    // 必要參數
    private final int id; // 所有屬性都被final修飾
    private final String name;

    // 可選參數
    private final int age;
    private final String gender;
    private final double height;
    private final int weight;

    private RobustPerson(Builder builder) { // 構造方法私有,即客戶端不能直接創建RobustPerson對象
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
        this.height = builder.height;
        this.weight = builder.weight;
    }

    public static final class Builder {
        // 必要參數
        private final int id; // 必要屬性被final修飾
        private final String name;

        // 可選參數
        private int age;
        private String gender;
        private double height;
        private int weight;

        public Builder(int id, String name) { this.id = id; this.name = name; } // 必要參數通過構造方法賦值

        public Builder age(int age) { this.age = age; return this; } // 可選參數通過同名方法賦值
        public Builder gender(String gender) { this.gender = gender; return this; }
        public Builder height(double height) { this.height = height; return this; }
        public Builder weight(int weight) { this.weight = weight; return this; }

        public RobustPerson build() {
            RobustPerson person = new RobustPerson(this);
            // 復雜業務語義校驗,對於校驗不通過場景,拋出異常
            if (person.height != 0 && person.weight != 0) { // Builder對象並非線程安全的,不能用this.xxx校驗
                double bmi = person.weight / (person.height * person.height);
                if (bmi < 18 || bmi > 25) { // 身體質量指數(BMI)低於18或高於25時表示不健康
                    throw new IllegalArgumentException(person.name+" NOT A ROBUST PERSON!");
                }
            }
            return person;
        }
    }

    public Builder toBuilder() { // 克隆
        return new Builder(this.id, this.name).age(this.age)
                .gender(this.gender).height(this.height).weight(this.weight);
    }

    @Override
    public String toString() {
        return name + "{" + "id=" + id + ", age=" + age + ", gender='" + gender + '\'' +
                ", height=" + height + "m, weight=" + weight + "kg}";
    }
}

測試類如下:

public class BuilderTest {
    public static void main(String[] args) {
        RobustPerson jack = new RobustPerson.Builder(1, "Jack")
                .age(18).gender("male").height(1.70).weight(65).build();
        System.out.println(jack);

        System.out.println("Jack keeps eating too much...");
        System.out.println(jack.toBuilder().weight(80).build());
    }
}

運行后輸出:

Jack{id=1, age=18, gender='male', height=1.7m, weight=65kg}
Jack keeps eating too much...
Exception in thread "main" java.lang.IllegalArgumentException: Jack NOT A ROBUST PERSON!
	at builder.RobustPerson$Builder.build(RobustPerson.java:48)
	at builder.BuilderTest.main(BuilderTest.java:14)

關鍵特點

結合上節示例代碼,可知Builder模式創建對象具有以下特點:

  • RobustPerson類的構造方法是私有的,即客戶端不能直接創建RobustPerson對象。
  • RobustPerson類不可變(線程安全的): 所有屬性都被final修飾,在構造方法中設置參數值,並且不對外提供Setter方法(Getter方法可選)。
  • 靜態內部類Builder與RobustPerson擁有相同的成員變量,且Builder內通過構造方法處理final修飾的必要參數,通過同名方法處理可選參數。
  • Builder內的build()方法調用RobustPerson的私有構造函數來創建RobustPerson對象,且客戶端只能通過該build()方法創建對象(從而避免Invalid狀態)。
  • Builder對象並不具有線程安全性。如果需要對RobustPerson對象的參數強加約束條件,應對build()方法所創建的RobustPerson對象進行檢驗。
  • 當創建多個對象且對象大多數屬性值都相同時,通過toBuilder()可簡單高效地克隆對象,僅針對不同的屬性重新設置值。
  • Builder模式使用鏈式調用,可讀性更佳。

但Builder模式也不可避免地存在自身的缺點。例如:

  • 創建對象前必須先創建它的構建器,消耗內存(若僅需要鏈式調用可仿照Builder類定義目標類)。
  • Builder模式存在冗長的樣板代碼(可借助InnerBuilder或Lombok插件自動生成)。

業界實踐

  • StringBuilder(JDK)
  • JobBuilder(quartz-2.3.0.jar)
  • SessionFactoryBuilder等(hibernate-core-5.3.6.Final.jar)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM