這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰java
歡迎來到今天的學習,今天咱們一塊兒來嘮嘮建造者模式。多嘮叨幾句,前面我有提到本月將會對java的設計模式精講,歡迎點擊頭像,關注個人專欄,我會持續更新,加油!算法
系列文章:編程
設計模式之單例模式設計模式
設計模式之工廠模式markdown
....系列持續更新框架
話很少話,進入正題ide
建造者模式是另一個高頻使用的建立型設計模式——一般叫 Builder 模式,中文通常叫建造者模式或生成器模式。函數
今天咱們改變下策略,先開始不講原理,先從例子下手,從解決問題案例當中去尋求答案。你認爲的那種答案。oop
先看建造者模式主要帶來什麼或者解決什麼問題:post
直接使用構造函數或者使用 set 方法來建立對象方不方便?,有沒有一種簡單的方法建立對象
存在的價值,爲何須要建造者模式建立對象
下面經過代碼具體深刻理解,咱們只講乾貨,讓你看完就能解決如今你代碼中的實際問題!
對同一種場景咱們用建造者模式和不用建造者模式來進行代碼對比:
很簡單,咱們建立學生對象
/** * 學生實體 * 不用建造者模式 */
public class Students {
private String name; //姓名
private int age; //年齡
private String grade; //班級
private String gender; //性別
public Student(String name, int age, String grade, String gender) {
this.name = name;
this.age = age;
this.grade = grade;
this.gender = gender;
}
public Student(String name, int age, String grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
//上述代碼寫了三個不一樣參數的構造函數
public static void main(String[] args) {
Student student01 = new Student("張三",26,"13班","男");
Student student02 = new Student("張三",26,"13班");
...
}
複製代碼
上述有問題嗎,沒有問題。這代碼能夠嗎,你以爲呢?問題顯而易見(確定還有不少小夥伴日常工做中都是這麼搞的)。上面這段代碼沒有使用建造者模式,因此咱們須要使用傳統的 getter、setter 方法,並指定不一樣的入參來構造對象。若是參數過多,業務比較多,那須要建立太多構造方法。
而使用建造者模式後的類,功能卻發生了徹底不同的變化,以下所示:
/** * 學生實體 * * 使用建造者模式建立對象 */
public class Students {
//全部屬性
private String name;
private int age;
private String grade;
private String gender;
public Students() {
}
//內部builder new對象
public static Students builder() {
return new Students();
}
//將屬性做爲步驟
public Students name(String name) {
this.name = name;
return this;
}
//將屬性做爲步驟
public Students age(int age) {
this.age = age;
return this;
}
//將屬性做爲步驟
public Students grade(String grade) {
this.grade = grade;
return this;
}
//將屬性做爲步驟
public Students gender(String gender) {
this.gender = gender;
return this;
}
//執行建立操做(最後封口return)
public Students build() {
validateObject(this);
return this;
}
private void validateObject(Students Students) {
//能夠作基礎預校驗,或自定義校驗
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", age=" + age +
", grade='" + grade + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
//用建造者模式來建立對象
public static void main(String[] args) {
//建立對象
Students st = Students.builder()
.name("張三")
.age("26")
.grade("13班")
.gender("男")
.build();
//建立對象
Students st01 = Students.builder()
.name("張三")
.age("26")
.grade("13班")
.build();
//一步步建造一個個屬性
}
複製代碼
這,就是建造者模式來搞定對象。咱們能夠發現構建出了徹底不一樣的對象實例,而使用傳統的 getter、setter 方法,則須要寫不少不一樣的構造函數來應對變化。
因此說,使用建造者模式能更方便地幫助咱們按需進行對象的實例化,避免寫不少不一樣參數的構造函數,同時還能解決同一類型參數只能寫一個構造函數的弊端。
雖然上面案例的實現比較簡單,可是也充分演示瞭如何使用建造者模式。在實際的使用中,一般能夠直接使用 lombok 的 @Builder 註解實現類自身的建造者模式
爲何要使用建造者模式來建立類?在我看來,有如下幾點緣由。
第一,分步驟的屬性賦值更適合屢次運算結果類建立場景。在面向對象軟件開發中,不少時候建立類所須要的參數並非一次都能準備好的,好比,計算訂單優惠價格、查詢庫存狀態等,有的參數可能須要經過調用多個服務運算後才能得出,這時咱們能夠根據已知參數預先對類進行建立,等有合適的參數時再設置類的屬性,而不是等到全部結果都齊備後纔去建立類。
第二,不須要關心特定類型的建造者的具體算法實現。 好比,咱們在使用 StringBuilder 時,並不太關心它的具體代碼實現,而是關心它提供給咱們的使用功能。這在某些須要快速複用的場景中,能起到提高編碼效率的做用。
第三,提升代碼的可閱讀性,簡潔明瞭,須要哪一個功能(屬性),就調用哪一個,方便閱讀,可維護性強。內部屬性擴展性極強,自由地組合對象屬性。
第四知足開閉原則,這也是設計模式的優勢。 每個建造者都相對獨立,所以能方便地進行替換或新增,這就大大提高了代碼的可擴展性。
固然,誇獎完,仍是要指出一些存在一些客觀性存在的問題。
使用範圍有限。 建造者模式所建立的對象通常都須要有不少的共同點,若是對象實例之間的差別性很大,則不適合使用建造者模式。
容易形成超類,業務繁多,新的建立步驟就會被加進來,這會形成代碼量的急劇膨脹,類會變得臃腫,最終造成一個龐大的超大類。
咱們來看下Netty框架當中是怎麼運用建造者模式的(Netty不懂的自行百度下,後續我會對Netty做出專題來說)。
你們還記得,在服務端啓動的時候有個啓動輔助類ServerBootStrap,咱們調用group方法、channel方法設置參數。這裏面也使用了這種建造者鏈式編程來設置相關參數。
ServerBootstrap b = new ServerBootstrap();
//開始建立對象
b.group(bossGroup, workerGroup) //bossGroup和workerGroup 先不用管,本節主要講建造者模式
//賦值channel參數
.channel(NioServerSocketChannel.class)
//賦值option參數
.option(ChannelOption.SO_BACKLOG, 100)
//賦值handler參數
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
System.out.println("服務啓動");
}
});
複製代碼
下面咱們來一塊兒看下源碼,源碼我就直接從idea當中截圖了,我會從中圈出重點:
先看group,設置完參數以後,便返回this對象
再看 childHandler,依然如此,是否是和咱們上面學生的例子是同樣的
其餘幾個參數也是同樣,你們能夠自行看下。
OK,今天咱們的建造者模式就講到這裏,感謝你的閱讀,相信你能從中學到東西。同時也很是歡迎您的點贊,關注和分享!
我已經將本章收錄在專題裏,點擊文章下方專題,關注專欄,我會天天發表乾貨,本月我會持續輸入設計模式。咱們下期再見!