1. 程式人生 > >深入探索Java設計模式(五)之構建器模式

深入探索Java設計模式(五)之構建器模式

抽絲剝繭 細說架構那些事——【優銳課】

簡單的程式不需要大量的設計過程,因為它們只關注有限的解決方案,僅使用幾個類。大型程式專注於廣泛的設計,該設計比好的設計範例的任何其他屬性都更能利用可重用性。巨集偉的想法不僅是為當前問題提供解決方案,而且是建立一種設計,為將來的變化奠定基礎。複雜的程式需要數千行程式碼以及物件和使用者之間的大量互動。這些型別的解決方案通常在使用數千臺櫃員機操作的空中交通管制系統和銀行系統中找到。本文是在學習完優銳課JAVA架構VIP課程—【框架原始碼專題】中《學習原始碼中的優秀設計模式》後寫下的學習感悟。在這裡,我們探索一種稱為“構建器模式”的設計模式,並使用Java程式碼示例對其進行實現。

深入探索Java設計模式(一)之單例模式

深入探索Java設計模式(二)之策略模式

深入探索Java設計模式(三)之裝飾器模式

深入探索Java設計模式(四)之享元模式

 

總覽

有效的軟體設計不僅可以滿足當前的要求,而且可以構成未來更改和開發的基礎。說起來比在實際應用中做起來容易。但是,設計模式無疑在很大程度上減輕了程式碼設計的負擔。模式是用於構建靈活且可維護的軟體的成熟架構。它通過一套標準的規範和實踐大大降低了程式碼的複雜性。

有許多可用的設計模式,開發人員可以根據程式碼流的最佳表達選擇一種。不選擇不符合你需求的產品幾乎是不可能的。實際上,設計模式是某人已經遇到問題並設計最佳實踐以獲得解決方案的證明。但是,它們絕不是天意。一個更好的主意可以隨時替換它們。在和平環境下的叛亂是自殺的。儘管人們可以擺脫困境並做自己的事,但在大多數情況下遵循某種設計模式是有幫助的。

 

構建器模式

設計模式根據其特徵命名。例如,構建器模式列出了構建類結構的規範。在例項化面向物件程式設計中的類時特別有用。想法是將複雜物件的構造與其表示分離。它利用靈活性來設計Java之類的物件。當我們開始編碼時,很容易感覺到這種設計模式的便利性。

 

使用構建器模式

此模式對於建立具有許多欄位或屬性的類的例項特別有用。顯而易見的是,在這種情況下,建構函式非常麻煩。例如,在這樣的類中:

 1 public class Person {
 2  
 3    private final long id;
 4    private final String firstName;
 5    private final String middleName;   //optional
 6    private final String lastName;     //optional
 7    private final Date birthDate;
 8    private final String phone;
 9    private final String email;
10  
11    private final String street;       //optional
12    private final String city;         //optional
13    private final String province;
14    private final String zip;
15    // ...

 

要建立此類的例項,我們可以:

  • 使用單個建構函式用值初始化欄位
  • 使用多個建構函式
  • 使用無引數建構函式例項化物件後,使用setter方法

儘管這些都是語法上有效的技術,但它們在實踐中非常麻煩。隨著欄位數量的增加,很快將變得難以管理和理解。使用單個建構函式是一個壞主意,首先是因為用龐大的引數化建構函式初始化許多欄位是一個不好的設計。其次,有一些選擇可以消除可選欄位。使用多個建構函式不是一個好主意,因為如果將來增加欄位的數量,它將很快變得難以管理。

第三種方法是根本不使用任何建構函式,而是從欄位中刪除final修飾符並使用setter方法進行初始化。該技術的問題在於,我們可以使用setter方法建立此類的無效物件。例如,以下內容儘管在語法上有效,但卻是該類在語義上無效的例項。

1 Person person = new Person();
2 person.setCity("Mumbai");

 

請注意,人員物件的定義不僅是城市欄位的有效初始化,而且至少是非可選欄位的正確初始化。這是setter方法初始化的真正問題。

 

實現構建器模式

我們可以使用構建器模式來克服上面討論的所有問題。在這裡,通過這種技術,我們建立了一個稱為生成器的伴隨物件。此配套物件用於構造合法域物件。這不僅提高了程式碼的清晰度,而且使構造變得簡單。

 1 public class Person {
 2    public static class Builder {
 3       private long id;
 4       private String firstName;
 5       private String middleName;     //optional
 6       private String lastName;       //optional
 7       private Date birthDate;
 8       private String phone;
 9       private String email;
10       private String street;         //optional
11       private String city;           //optional
12       private String province;
13       private String zip;
14       public Builder id(final long id) {
15          this.id = id;
16          return this;
17       }
18       public Builder firstName(final String firstName) {
19          this.firstName = firstName;
20          return this;
21       }
22       public Builder middleName(final String middleName) {
23          this.middleName = middleName;
24          return this;
25       }
26       public Builder lastName(final String lastName) {
27          this.lastName = lastName;
28          return this;
29       }
30       public Builder birthDate(final Date birthDate) {
31          this.birthDate = birthDate;
32          return this;
33       }
34       public Builder phone(final String phone) {
35          this.phone = phone;
36          return this;
37       }
38       public Builder email(final String email) {
39          this.email = email;
40          return this;
41       }
42       public Builder street(final String street) {
43          this.street = street;
44          return this;
45       }
46       public Builder city(final String city) {
47          this.city = city;
48          return this;
49       }
50       public Builder province(final String province) {
51          this.province = province;
52          return this;
53       }
54       public Builder zip(final String zip) {
55          this.zip = zip;
56          return this;
57       }
58       public Person build(){
59          if(id <= 0 || firstName.isEmpty() ||
60                birthDate == null || phone.isEmpty() ||
61                email.isEmpty() || province.isEmpty() ||
62                zip.isEmpty()){
63             throw new IllegalStateException("Cannot create
64                Person object.");
65       }
66       return new Person(id,firstName,middleName,lastName,
67          birthDate,phone,email,street,city,province,zip);
68       }
69    }
70    private final long id;
71    private final String firstName;
72    private final String middleName;     //optional
73    private final String lastName;       //optional
74    private final Date birthDate;
75    private final String phone;
76    private final String email;
77    private final String street;         //optional
78    private final String city;           //optional
79    private final String province;
80    private final String zip;
81    private Person(final long id, final String firstName,
82          final String middleName, final String lastName,
83          final Date birthDate, final String phone,
84          final String email, final String street,
85          final String city, final String province,
86          final String zip) {
87       this.id = id;
88       this.firstName = firstName;
89       this.middleName = middleName;
90       this.lastName = lastName;
91       this.birthDate = birthDate;
92       this.phone = phone;
93       this.email = email;
94       this.street = street;
95       this.city = city;
96       this.province = province;
97       this.zip = zip;
98    }
99 }

 

Builder類是Person類的一部分,用於構造Person物件。對於建構函式,引數以特定方式排序。結果,它們以相同順序傳遞。使用構建器模式時,順序無關緊要,並且可以在構造過程中以任何順序傳遞值。請注意,在這種情況下,建構函式被設為私有。

 1 @Test
 2 public void rightBuild() {
 3    final Person.Builder builder = new Person.Builder();
 4    final Person emp = builder
 5       .id(101)
 6       .firstName("Percy")
 7       .middleName("Bysshe")
 8       .lastName("Shelley")
 9       .birthDate(new GregorianCalendar(1792,
10          Calendar.AUGUST,4).getTime())
11       .phone("1234567890")
12       .email("[email protected]")
13       .street("123 somewhere")
14       .province("someplace")
15       .zip("10293847").build();
16 }
17 @Test(expected = IllegalStateException.class)
18 public void wrongBuild() {
19    final Person.Builder builder = new Person.Builder();
20    final Person emp = builder
21       .middleName("Bysshe")
22       .lastName("Shelley")
23       .phone("1234567890")
24       .zip("10293847").build();
25 }

 

在測試方法中觀察我們如何通過呼叫builder方法和一系列方法呼叫來建立物件。最後,呼叫build()方法以結束鏈並完成物件的建立。這就是我們用Java程式碼實現構建器模式的方式。

 

結論

本質是瞭解構建器模式背後的原理並以自己的方式實現。但是,在所有情況下,模式幾乎都保持不變。如指定的那樣,在必須初始化類中的大量欄位的情況下,構建器模式特別有用。每個類都不適合使用此模式。可以看出,為方便起見,程式碼行增加了。明智地謹慎使用它。

 感謝閱讀!歡迎留言。想更深入探討學習也歡迎私信