1. 程式人生 > >設計模式實戰系列之@Builder和建造者模式

設計模式實戰系列之@Builder和建造者模式

## 前言 備受爭議的`Lombok`,有的人喜歡它讓程式碼更整潔,有的人不喜歡它,巴拉巴拉一堆原因。在我看來`Lombok`唯一的缺點可能就是需要安裝外掛了,但是對於業務開發的專案來說,它的優點遠遠超過缺點。 > 我們可以看一下,有多少專案使用了Lombok(數量還在瘋漲中...) ![](https://img.iisheng.cn/lombok-used-by.png) > 儘管如此,我們今天也只是單純的來看一下@Builder()這個東西 ## @Builder的使用 ### 使用@Builder修飾類 ```Java @Data @Builder public class UserDO { private Long id; private String name; } ``` ### 使用建造者模式建立類 ```Java @Test public void test() { UserDO userDO = UserDO.builder() .id(1L) .name("iisheng") .build(); System.out.println(userDO); } ``` ## 編譯後原始碼 執行`javac -cp ~/lombok.jar UserDO.java -verbose`將`.java`編譯成`.class`檔案。 通過IDE檢視該`.class`原始碼 > 下面展示的是被我處理後的原始碼,感興趣的同學,可以自己執行上面命令,檢視完整原始碼 ```Java public class UserDO { private Long id; private String name; public String toString() { return "UserDO(id=" + this.getId() + ", name=" + this.getName() + ")"; } UserDO(Long var1, String var2) { this.id = var1; this.name = var2; } public static UserDO.UserDOBuilder builder() { return new UserDO.UserDOBuilder(); } private UserDO() { } public static class UserDOBuilder { private Long id; private String name; UserDOBuilder() { } public UserDO.UserDOBuilder id(Long var1) { this.id = var1; return this; } public UserDO.UserDOBuilder name(String var1) { this.name = var1; return this; } public UserDO build() { return new UserDO(this.id, this.name); } } } ``` 由此,我們可以看出來Builder的實現步驟: - 在`UserDO`中建立靜態`UserDOBuilder` - 編寫設定屬性方法,返回`UserDOBuilder`物件 - 編寫`build()`方法,返回`UserDO`物件 是不是很簡單?我曾經看過不知道哪個大佬說的一句話,整潔的程式碼不是說,行數更少,字數更少,而是閱讀起來邏輯更清晰。所以,我覺得,哪怕我們不用@Builder,也應該多用這種建造者模式。 > 是時候看看什麼是建造者模式了! ## 建造者模式 ### UML類圖 > 這是大部分書籍網路中的建造者模式類圖 ![](https://img.iisheng.cn/builder-pattern.png) ### 產品類 ```Java public class Product { private String name; private Integer val; Product(String name, Integer val) { this.name = name; this.val = val; } @Override public String toString() { return "Product is " + name + " value is " + val; } } ``` ### 抽象建造者 ```Java public abstract class Builder { protected Integer val; protected String name; // 設定產品不同部分,以獲得不同的產品 public abstract void setVal(Integer val); // 設定名字 公用方法 public void setName(String name) { this.name = name; } // 建造產品 public abstract Product buildProduct(); } ``` ### 具體建造者 ```Java public class ConcreteBuilder extends Builder { @Override public void setVal(Integer val) { /** * 產品類內部的邏輯 * 實際儲存的值是 val + 100 */ this.val = val + 100; } @Override // 組建一個產品 public Product buildProduct() { // 這塊還可以寫特殊的校驗邏輯 return new Product(name, val); } } ``` ### 導演類 ```Java public class Director { private Builder builder = new ConcreteBuilder(); public Product getAProduct() { // 設定不同的零件,產生不同的產品 builder.setName("ProductA"); builder.setVal(2); return builder.buildProduct(); } } ``` > 我更喜歡這樣的建造者模式類圖 ![](https://img.iisheng.cn/modified-builder.png) `Product`的建立,也依賴於`Builder`。程式碼只需要將上面的`Product`和`ConcreteBuilder`調整一下即可。 ### 調整後的產品類 ```Java public class Product { private String name; private Integer val; Product(Builder builder) { this.name = builder.name; this.val = builder.val; } @Override public String toString() { return "Product is " + name + " value is " + val; } } ``` 這程式碼只是將構造方法改了,使用`Builder`來建立`Product`物件。 ### 調整後的具體建造者 ```Java public class ConcreteBuilder extends Builder { @Override public void setVal(Integer val) { /** * 產品類內部的邏輯 * 實際儲存的值是 val + 100 */ this.val = val + 100; } @Override // 組建一個產品 public Product buildProduct() { // 這塊還可以寫特殊的校驗邏輯 return new Product(this); } } ``` 相應的使用帶`Builder`的`Product`的構造方法。 ## JDK中的建造者模式 > StringBuilder (擷取部分原始碼) ### 抽象建造者 ```Java abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value; /** * The count is the number of characters used. */ int count; public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append(CharSequence s) { if (s == null) return appendNull(); if (s instanceof String) return this.append((String)s); if (s instanceof AbstractStringBuilder) return this.append((AbstractStringBuilder)s); return this.append(s, 0, s.length()); } public AbstractStringBuilder delete(int start, int end) { if (start < 0) throw new StringIndexOutOfBoundsException(start); if (end > count) end = count; if (start > end) throw new StringIndexOutOfBoundsException(); int len = end - start; if (len > 0) { System.arraycopy(value, start+len, value, start, count-end); count -= len; } return this; } } ``` ### 具體建造者 ```java public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { @Override public StringBuilder append(String str) { super.append(str); return this; } @Override public StringBuilder append(CharSequence s) { super.append(s); return this; } /** * @throws StringIndexOutOfBoundsException {@inheritDoc} */ @Override public StringBuilder delete(int start, int end) { super.delete(start, end); return this; } } ``` `StringBuilder`中的建造者模式比較簡單,但是我的確沒找到`StringBuilder`非要用建造者模式的原因,或許就是想讓我們寫下面這樣的程式碼? ```Java public static void main(String[] args) { StringBuilder sb = new StringBuilder(); sb.append("Love ") .append("iisheng !") .insert(0, "I "); System.out.println(sb); } ``` > 但是我希望你能通過`StringBuilder`,感受一下建造者模式的氣息 ## Guava Cache中的建造者模式 ### 如何使用 Guava Cache? ```Java public static void main(String[] args) { Load