前言 近日,看到Myabtis中組件中SqlSessionFactory由SqlSessionFactoryBuilder().build()生成時,且採用Builder模式,遂記錄學習之。 1、什麼是Builder模式? (1)對於複雜的對象,如果只是用構造方法創建的話,構造方法中會存在很多的邏輯 ...
前言
近日,看到Myabtis中組件中SqlSessionFactory由SqlSessionFactoryBuilder().build()生成時,且採用Builder模式,遂記錄學習之。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
1、什麼是Builder模式?
(1)對於複雜的對象,如果只是用構造方法創建的話,構造方法中會存在很多的邏輯,那麼我們可以一步步有秩序構建它,從而降低複雜度;
(2)對於複雜的對象,使用者不必知道其內部是如何實現的清下,逐步構造需要的實例對象;
2、什麼情況下使用Builder模式?
(1)將一個複雜對象的構建與它的表示分離,即相同的構造過程可以有不同表示;
(2)當有多個構造器且需要傳入不同的參數表示不同的產品時(即可以彌補工廠模式等無法選擇多參數的缺點)
(3)傳入參數情況比較靈活且複雜的情況,或者說一開始不需要明確參數的情況。
(4)框架中很多Configuration配置都會用到Builder模式。
3、具體使用Builder例子
(1)以前經常通過不同構造器傳入不同的參數構造不同複雜的對象,比如我們現在需要一個User的不同情況對象
- 只有id和name
- 有id、name、age
- 有id、name、age、address
public class User { private int id; private String name; private int age; private String address; //不同的構造器傳入不同的參數,創造不同的複雜的產品 public User(int id, String name) { this.id = id; this.name = name; } public User(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public User(int id, String name, int age, String address) { this.id = id; this.name = name; this.age= age; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
測試類這樣編寫:
public class Main { public static void main(String[] args) { //情況1:id、姓名 User user = new User(1, "Zhangsan"); //情況2:id、姓名、年齡 User user2 = new User(2,"Lisi",22); //情況3:id、姓名、年齡、地址 User user3 = new User(3,"Wangwu",23,"Beijing"); //情況4:id與年齡不清楚,很容易混淆,必須得查看源碼或者文檔才能知道哪個參數位置id/年齡 User user4 = new User(24,"Wangwu",24,"Beijing"); } }
OK,這樣是沒有問題的,但是會有以下弊端:
- 就好比情況4。當傳入的參數很多並且沒有說明文檔的情況下,之後閱讀完源碼後才能更好地使用。那麼就有可能對源碼進行修改,這樣就違背了在應用中一個好的設計模塊的原則,即設計模式中的開閉原則(Open-Closed Principle, OCP,對修改封閉)
- 每種情況都得編寫一個的構造器,沒有一點的靈活度。再比如這裡address屬性是可選的,可以不傳入,那麼靈活度同樣很低!
(2)接下來就使用Builder模式創建,註意:Builder主要採用Java靜態內部類
/** * 利用Builer模式靈活面對複雜對象的創建 * @author Lijian * */ public class User2 { private int id; private String name; private int age; private String address; private User2(Builder builder) { this.id = builder.id; this.name = builder.name; this.age = builder.age; this.address = builder.address; } static class Builder{ private int id; private String name; private int age; private String address; //靈活選擇參數 public Builder setId(int id) { this.id = id; return this; } public Builder setName(String name) { this.name = name; return this; } public Builder setAddress(String address) { this.address = address; return this; } public Builder setAge(int age) { this.age = age; return this; } //最後build返回User2對象 public User2 build() { return new User2(this); } } }
測試類:
public class Main { public static void main(String[] args) { //通過build創建了User2對象,之後通過setXXX方法可靈活初始化屬性,最後build返回對象 User2 user = new User2.Builder().setId(1).setName("Lijian").setAge(22).build(); //情況1:id、姓名 User2 user2 = new User2.Builder().setId(2).setName("Zhangsan").build(); //情況2:id、姓名、年輕、地址 User2 user3 = new User2.Builder().setId(2).setName("Lisi").build(); //情況3:id與age很明顯能區分 User2 user4 = new User2.Builder().setId(23).setAge(23).build(); } }
通過setXXX()方法靈活選擇參數,最後build()方法“閉合”返回對象。很適用於複雜對象的創建,此處讓我想起Java8新特性中的Stream API(https://blog.csdn.net/mynewclass/article/details/80393308)的一個特點:懶載入。是的,有點“懶載入”的味道,不需要立馬指定屬性,也不會立馬生效,之後最後的操作build()才會生效!