Java 高效程式設計之 Builder 模式

JAVA架構開發發表於2018-09-14

前言

在《Effective Java 第2版》中有提到,遇到多個構造器引數時要考慮使用構建器(Builder模式)。相比於重疊構造器(telescoping constructor)模式和JavaBeans模式,Builder模式實現的物件更利於使用。 下面從一個Person例子進行分析以上三種設計模式的使用,Person類有兩個必要引數(id和name),有5個可選引數(age,sex,phone,address和desc)

如果你依然在程式設計的世界裡迷茫,不知道自己的未來規劃,可以加入JAVA架構學習交流群:835544715 裡面可以與大神一起交流並走出迷茫。進群免費領取學習資料,看看前輩們是如何在程式設計的世界裡傲然前行!群裡不停更新最新的教程和學習方法(進群送JAVA架構視訊資料),有想學習JAVA的,或是轉行,還有工作中想提升自己能力的,正在學習的小夥伴歡迎加入學習

1 重疊構造器模式

我們先來看看程式設計師一向習慣使用的重疊構造器模式,在這種模式下,你提供第一個只有必要引數的構造器,第二個構造器有一個可選引數,第三個有兩個可選引數,依此類推,最後一個構造器包含所有的可選引數。下面看看其程式設計實現:

/**
 * 使用重疊構造器模式
 */
public class Person {   
    //必要引數
    private final int id;
    private final String name;
    //可選引數
    private final int age;
    private final String sex;
    private final String phone;
    private final String address;
    private final String desc;

    public Person(int id, String name) {
        this(id, name, 0);
    }

    public Person(int id, String name, int age) {
        this(id, name, age, "");
    }

    public Person(int id, String name, int age, String sex) {
        this(id, name, age, sex, "");
    }

    public Person(int id, String name, int age, String sex, String phone) {
        this(id, name, age, sex, phone, "");
    }

    public Person(int id, String name, int age, String sex, String phone, String address) {
        this(id, name, age, sex, phone, address, "");
    }

    public Person(int id, String name, int age, String sex, String phone, String address, String desc) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
        this.desc = desc;
    }
}
複製程式碼

從上面的程式碼中,當你想要建立例項的時候,就利用引數列表最短的構造器,但該列表中包含了要設定的所有引數:
Person person = new Persion(1, "李四", 20, "男", "18800000000", "China", "測試使用重疊構造器模式");
這個構造器呼叫通常需要許多你本不想設定的引數,但還是不得不為它們傳遞值。
一句話:重疊構造器可行,但是當有許多引數的時候,建立使用程式碼會很難寫,並且較難以閱讀。

2 JavaBeans模式

遇到許多構造器引數的時候,還有第二種代替辦法,即JavaBeans模式。在這種模式下,呼叫一個無參構造器來建立物件,然後呼叫setter辦法來設定每個必要的引數,以及每個相關的可選引數:

/**
 * 使用JavaBeans模式
 */
public class Person {
    //必要引數
    private int id;
    private String name;
    //可選引數
    private int age;
    private String sex;
    private String phone;
    private String address;
    private String desc;

    public void setId(int id) {
        this.id = id;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}
複製程式碼

這種模式彌補了重疊構造器模式的不足。建立例項很容易,這樣產生的程式碼讀起來也很容易:
Person person = new Person();
person.setId(1);
person.setName("李四");
person.setAge(20);
person.setSex("男");
person.setPhone("18800000000");
person.setAddress("China");
person.setDesc("測試使用JavaBeans模式");
遺憾的是,JavaBeans模式自身有著很重要的缺點。因為構造過程被分到了幾個呼叫中,在構造過程中JavaBean可能處於不一致的狀態。類無法僅僅通過檢驗構造器引數的有效性來保證一致性。

3 Builder模式(推薦) 幸運的是,還有第三種替代方法,既能保證像重疊構造器模式那樣的安全性,也能保證像JavaBeans模式那麼好的可讀性。這就是Builder模式的一種形式,不直接生成想要的物件,而是讓客戶端利用所有必要的引數呼叫構造器(或者靜態工廠),得到一個builder物件。然後客戶端在builder物件上呼叫類似於setter的方法,來設定每個相關的可選引數。最後,客戶端呼叫無參的builder方法來生成不可變的物件。這個builder是它構建類的靜態成員類。下面就是它的示例:

/**
 * 使用Builder模式
 */
public class Person {
    //必要引數
    private final int id;
    private final String name;
    //可選引數
    private final int age;
    private final String sex;
    private final String phone;
    private final String address;
    private final String desc;

    private Person(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.phone = builder.phone;
        this.address = builder.address;
        this.desc = builder.desc;
    }

    public static class Builder {
        //必要引數
        private final int id;
        private final String name;
        //可選引數
        private int age;
        private String sex;
        private String phone;
        private String address;
        private String desc;

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

        public Builder age(int val) {
            this.age = val;
            return this;
        }

        public Builder sex(String val) {
            this.sex = val;
            return this;
        }

        public Builder phone(String val) {
            this.phone = val;
            return this;
        }

        public Builder address(String val) {
            this.address = val;
            return this;
        }

        public Builder desc(String val) {
            this.desc = val;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }
}
複製程式碼

想要學習Java高架構、分散式架構、高可擴充套件、高效能、高併發、效能優化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分散式專案實戰學習架構師視訊免費獲取 架構群:835544715

點選連結加入群聊【JAVA高階架構】:jq.qq.com/?_wv=1027&a…

注意Person是不可變得,所有的預設引數值都單獨放在一個地方。builder的setter方法返回builder本身。以便可以把連線起來。下面是客戶端使用程式碼:

/**
 * 測試使用
 */
public class Test {

    public static void main(String[] args) {
        Person person = new Person.Builder(1, "張三")
                .age(18).sex("男").desc("測試使用builder模式").build();
        System.out.println(person.toString());
    }
}
複製程式碼

Java 高效程式設計之 Builder 模式
這樣的客戶端程式碼很容易編寫,更為重要的是,易於閱讀。

相關文章