Effective Java(一)———— 代替构造器和Setter的构建器模式
引言
Java語言中的一部經(jīng)典著作《Effective Java》,里面涵蓋了78條我們應(yīng)該熟練的Java編程技巧。
本篇博客是該書學習的系列筆記第一篇。本系列博客不會與書中的78條建議完全匹配。只是以一種讀者的身份來記錄和總結(jié)從書中得到的好的編程建議,博客中會明確從書中哪條建議得來的知識。
本博客總結(jié)自書中第二條:遇到多個構(gòu)造器參數(shù)時要考慮用構(gòu)建器
構(gòu)建器的產(chǎn)生
我們知道對象的構(gòu)建有很多種方式:構(gòu)造器、靜態(tài)工廠、Setter。
通常,最傻瓜式的對象構(gòu)建方式是:通過new關(guān)鍵字,調(diào)用一個默認的無參構(gòu)造,然后向這個對象中set各種屬性。
第二種稍微上點檔次的方式是使用構(gòu)造器。為一個JavaBean創(chuàng)建一個有參構(gòu)造,在new的時候通過有參構(gòu)造來構(gòu)建這個bean。
第三種是靜態(tài)工廠,簡單的說也是通過一個靜態(tài)方法,來調(diào)用一個有參構(gòu)造,來構(gòu)建對象。
這三種方法貌似都沒什么問題。但是設(shè)想一下這樣的場景:
一個JavaBean中有很多屬性,大于5個,這些屬性有些是必需的,有些是可選的,如何來選擇構(gòu)建對象的方式?
上述三種方式:有參構(gòu)造、靜態(tài)工廠、Setter屬性,依然可以達到場景的要求。但是它們在面對大量可選參數(shù)的情況下并不能很好的擴展。原因如下:
有參構(gòu)造(或靜態(tài)工廠):需要提供針對可選參數(shù)的多個不同參數(shù)列表的構(gòu)造器,從而滿足不同場景下對可選參數(shù)的賦值,不同構(gòu)造器之間參數(shù)列表的差別很細微,可讀性非常差,這種方式叫“重疊構(gòu)造器”。
Setter屬性:這種方式雖然比較靈活,但是有可能有線程安全問題,因為在調(diào)用構(gòu)造器與set方法之間,對象并未完全構(gòu)建完畢,這個時候如果在多線程的環(huán)境下有可能使未完成構(gòu)建的對象處于一種不安全的狀態(tài)。這種方式叫“JavaBeans模式”。
總之一句話:重疊構(gòu)造器(或重疊靜態(tài)工廠)可以構(gòu)造出線程安全的JavaBean,但是可讀性差,擴展性不強;而JavaBeans模式,雖然可讀性好,靈活,但是不具備線程安全性,可能發(fā)生線程安全問題。
這就產(chǎn)生了新的構(gòu)造對象的方式:構(gòu)建器。它是“建造者模式”的一種形式,同時具備靈活性、線程安全性、可讀性等多種優(yōu)點,但是可能也要面臨額外的編碼量、理解難度、可能存在的性能開銷等問題。一般在某個對象含有大量可選參數(shù)的情況下使用構(gòu)建器。
構(gòu)建器的使用
說了這么多,到底構(gòu)建器是什么?我們?nèi)绾问褂脴?gòu)建器呢?
首先需要明確一點,構(gòu)建器是用于構(gòu)建一個對象的。它兼顧了構(gòu)造器的優(yōu)點和setter的優(yōu)點。
客戶端使用一個構(gòu)建器需要完成三件事:
1、不直接生成目標對象,而是讓客戶端代碼利用所有必需參數(shù)調(diào)用構(gòu)造器(或靜態(tài)工廠)創(chuàng)建builder對象(builder對象用于后續(xù)目標對象的創(chuàng)建和賦值工作)。
2、客戶端在builder對象上調(diào)用類似Setter方法,來設(shè)置每個可選參數(shù)。
3、客戶端調(diào)用無參的builder()方法生成目標對象。
?因此,客戶端代碼可以像下面這樣:
// 客戶端代碼使用必需參數(shù)創(chuàng)建一個builder對象 Student stu = new Student.StudentBuilder(10001, "張三", 1, 17)// 必須參數(shù):學號、姓名、性別、年齡.headTeacherName("張老師") // 可選參數(shù):班主任名稱.className("三年一班") // 可選參數(shù):班級名稱.address("朝陽區(qū)-潘家園") // 可選參數(shù):家庭住址.specialty("硬筆書法") // 可選參數(shù):特長.build();?可以看到上面的代碼中,一條語句就完成了對學生對象的創(chuàng)建,沒有使用setter方法將可選參數(shù)的設(shè)置與構(gòu)造器分開,因此不會存在線程安全問題,可讀性也非常強,可選參數(shù)可以根據(jù)不同的場景自由剪裁,因此擴展性也非常高。
構(gòu)建器的創(chuàng)建
和構(gòu)造器和Setter方法一樣,我們同樣需要在定義JavaBean對象的類中定義一個構(gòu)建器,讓它來完成對目標對象的構(gòu)建工作。
同樣,構(gòu)建器的編碼工作我們需要注意幾個關(guān)鍵步驟:
1、構(gòu)建器實際上是一個靜態(tài)成員類
2、構(gòu)建器類中擁有所有目標對象的屬性
3、構(gòu)建器類擁有設(shè)置必需參數(shù)的構(gòu)造器和設(shè)置每個可選參數(shù)的“類似Setter方法”
4、“類Setter方法”返回構(gòu)建器對象本身
5、構(gòu)建器有一個無參的build()方法,用于創(chuàng)建目標對象
6、目標類有唯一的私有構(gòu)造器,構(gòu)造器參數(shù)就是構(gòu)建器對象,這個構(gòu)造器的工作就是將參數(shù)中的每個屬性一 一賦值給目標對象。
和前面客戶端創(chuàng)建Student對象對應(yīng)的Student類代碼如下:
public class Student {// 必需參數(shù)private int stuNo;private String name;private int gender;// 性別:0代表女生;1代表男生private int age; // // 可選參數(shù)private String headTeacherName;// 班主任名稱private String address;// 家庭住址private String className;//班級名稱private String specialty;//特長private Student(StudentBuilder builder) {this.stuNo = builder.stuNo;this.name = builder.name;this.gender = builder.gender;this.age = builder.age;this.headTeacherName = builder.headTeacherName;this.address = builder.address;this.className = builder.className;this.specialty = builder.specialty;}/** 用于構(gòu)建Student對象的構(gòu)建器類*/public static class StudentBuilder {// 必需private int stuNo;private String name;private int gender;private int age;// 可選private String headTeacherName;// 班主任名稱private String address;// 家庭住址private String className;//班級名稱private String specialty;//特長public StudentBuilder(int stuNo, String name, int gender, int age) {this.stuNo = stuNo;this.name = name;this.gender = gender;this.age = age;}public StudentBuilder headTeacherName(String headTeacherName) {this.headTeacherName = headTeacherName;return this;}public StudentBuilder address(String address) {this.address = address;return this;}public StudentBuilder className(String className) {this.className = className;return this;}public StudentBuilder specialty(String specialty) {this.specialty = specialty;return this;}public Student build() {return new Student(this);}} }建議給Student添加一個toString()方法,并執(zhí)行一下客戶端代碼:通過構(gòu)建器創(chuàng)建Student對象并輸出它。
可以看到,使用構(gòu)建器的方式確實增加了一部分編碼量,因此,并不是以后所有的情況都是用構(gòu)建器模式才算好的代碼。這種構(gòu)建器模式通常用于參數(shù)較多的情況下。
總結(jié)
構(gòu)建器可以解決因為大量可選參數(shù)使用重疊構(gòu)造器導致客戶端代碼編碼困難、代碼可讀性差、擴展性不強等缺點;同時具備類似Setter的靈活度和可讀性。
同時,剖析了構(gòu)建器實現(xiàn)的本質(zhì)后,我們是否也可以簡化這種構(gòu)建器實現(xiàn)方式呢?當然可以,一個簡單的JavaBean,讓它的有參構(gòu)造傳入所有必需的參數(shù),而它的Setter方法將void改為返回當前JavaBean對象不就完全可以達到這種串行構(gòu)建對象的效果嗎?書中構(gòu)建器后半部分的闡述中提到了強加驗證的思想,但并沒有提供代碼的實現(xiàn)思路和案例,因此在真正使用構(gòu)建器的時候我們依然可以繼續(xù)深入的研究構(gòu)建器的使用方式,真正的掌握構(gòu)建器的使用。
以上就是構(gòu)建器的學習和使用,歡迎大家文末留言討論。
總結(jié)
以上是生活随笔為你收集整理的Effective Java(一)———— 代替构造器和Setter的构建器模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL数据库知识点总结
- 下一篇: 多媒体计算机技术的主要特点,多媒体技术主