Effective Java -- 遇到多個構造器參數時要考慮用構建器(Builder)__Java

來源:互聯網
上載者:User

本文是 《Effective Java》的讀書筆記,由於是Java進階書,難免會有理解的偏差,如有錯誤,非常歡迎能批評指正,本人不勝感激。

什麼是構建器(Builder)呢,參加過面試的同學可能會有被問到String和StringBuffer和StringBuilder的區別,這裡就有StringBuilder這個字串構建器,我們來簡單看一下這個構建器的用法吧。

        String sb = new StringBuilder().append("I ")                .append("am ")                .append("student.").toString();

我們看一下這裡用的append()函數的源碼

    @Override    public StringBuilder append(String str) {        super.append(str);        return this;    }

可知這個append()將傳入的str通過調用父類的方法加到了該對象字串的後面,並將該對象返回了,這樣就可以連續的調用append()方法,並且保持對象的唯一性(String每次都會建立一個新的對象)。

在父類方法中使用String的getChars()方法,將我們傳入的str值添加的字元數組value中去。

    public AbstractStringBuilder append(String str) {        if (str == null)            return appendNull();        int len = str.length();        ensureCapacityInternal(count + len);        //將 str 添加到字元數組 value 中去        str.getChars(0, len, value, count);        count += len;        return this;    }

[注]. 如想瞭解getChars()的工作原理,可以查看String的JDK源碼,這裡不再說明。

然後我們再來看一下最後的toString()方法

    @Override    public String toString() {        // Create a copy, don't share the array        return new String(value, 0, count);    }

這個方法就會通過value這個字元數組裡面的值產生一個String返回過去。

上面我們我們可能對這個Builder有了一個初步的模糊的瞭解,接下來我就舉一個更加容易懂的例子,看一看這個Builder的好處。

設想一個UserInfo類,包含如下欄位

屬性 說明 是否必要
userId 主鍵,使用者ID
userName 使用者姓名
userPosition 使用者職位(Java程式員一定為男性)
userSex 性別
userAge 年齡
userEmail 郵箱

可知,在這個類中使用者ID,使用者姓名是必要的,其他的是非必要的。

在這種參數相對比較多的情況下,使用建構函式建立執行個體,在用戶端的代碼就會顯得比較麻煩,會分不清建構函式的哪一個參數對應哪一個值。

當然,我們可以為每個參數提供一個setter方法,但是這種方式也有兩種不足。
(1)因為構造過程被分到了幾次調用中,所以在構造過程中可能會處於不一致的狀態。類無法僅僅通過檢驗構造器參數的有效性來保證一致性。

[注]對於這句話,我個人的理解如下
比如上面的使用者類,我們規定如果職位為Java開發工程師,則性別一定是男性。
但是由於setter方法是分成兩部進行設定值,而且設定值的順序也是不一致的,所以就無法使兩個參數同時滿足上面的要求。

(2)對於一些final類是無法使用setter方法的。

那麼有沒有一種方法不僅能滿足重疊構造器的安全性,又能滿足像使用setter那樣的易讀性呢。

這時候我們的構建器(Builder)就閃耀登場了。使用Builder分成下面的三步: 使用所有的必要欄位(如上使用者ID,使用者姓名)產生一個Builder 在Builder上利用setter方法設定一些非必要的屬性(可以一次設定一個或多個,如使用者性別和職位的關聯,不滿足就拋異常)。 調用無參的builder()方法產生不可變的對象。(如StringBuilder中的為toString()方法)。

執行個體代碼如下

package com.blog.effective2;/** * @func 使用構建器(Builder) * Created by 張俊強~ on 2017/10/30. */public class UserInfo {    private final String userId;        //required    private final String userName;      //required    private final String userPosition;    private final String userSex;    private final String userAge;    private final String userEmail;    public static class Builder {        private final String userId;        //required        private final String userName;      //required        private String userPosition = "";        private String userSex = "";        private String userAge = "";        private String userEmail = "";        public Builder setUserEmail(String email) {            userEmail = email;            return this;        }        public Builder setUserSexAndPosition(String sex, String position) {            //出現性別不是[男]的[Java程式員],則拋異常            if ((!"男".equals(sex)) && "Java程式員".equals(position)) {                throw new RuntimeException("參數匹配異常");            }            userSex = sex;            userPosition = position;            return this;        }        public Builder setUserAge(String age) {            userAge = age;            return this;        }        public Builder(String userId, String userName) {            this.userId = userId;            this.userName = userName;        }        //最後的 build 方法        public UserInfo build(){            return new UserInfo(this);        }    }    public UserInfo(Builder builder) {        this.userId=builder.userId;        this.userName=builder.userName;        this.userPosition=builder.userPosition;        this.userAge = builder.userAge;        this.userEmail = builder.userEmail;        this.userSex = builder.userSex;    }    @Override    public String toString() {        return "UserInfo{" +                "userId='" + userId + '\'' +                ", userName='" + userName + '\'' +                ", userPosition='" + userPosition + '\'' +                ", userSex='" + userSex + '\'' +                ", userAge='" + userAge + '\'' +                ", userEmail='" + userEmail + '\'' +                '}';    }}

有如下的測試代碼

    public static void main(String[] args) {        // 格式與 StringBuilder 是一致的.        UserInfo userInfo=new UserInfo.Builder("001","張三").setUserAge("23")                .setUserEmail("test@test.com")                .setUserSexAndPosition("男","Java程式員").build();        System.out.println(userInfo.toString());    }    //輸出結果    //UserInfo{userId='001', userName='張三', userPosition='Java程式員', userSex='男', userAge='23', userEmail='test@test.com'}

在這種模式下,由於在建立對象的同時需要建立相應的Builder對象,產生額外的花銷。

簡而言之,如果類的有多個參數時,可以考慮使用Builder模式(在該類中有很多參數是不必要的情況下,Builder模式表現的就更好)。

2017/10/30 13:05 於 福州大學.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.