1. 程式人生 > >我要打十個!詳解建造者模式(builder pattern)

我要打十個!詳解建造者模式(builder pattern)

### 前言 “我要打十個”,其實是我要打十個野怪! 這十個野怪呢,它們有不同的技能、裝備和武器,長得也不一樣。這裡野怪是一個蠻複雜的物件,由各個不同的部分組成(技能、裝備、武器等),不同的野怪的它們各部分之間的構成方式就不同。因此,要建立這種複雜物件,就需要使用建造者模式。 ### 什麼是建造者模式 首先建造者模式Gof 23種設計模式之一。也叫Builder模式。 **是將一個複雜物件的構建和其表示相分離,使得同樣的構建過程可以建立不同的表示。** 我們來品一品這句話,首先是複雜物件,這個複雜物件中可能包含了多個不同的其他物件。其次是這個複雜物件的建立一定是用到了這些其他物件,通過一定的演算法組合能才創建出這個物件。最後就是它能通過builder創建出一些特性不同但相似的物件。 好了,借用Linus 名言: **Talk is cheap. Show me the code!!!** ![表情包圖](https://img-blog.csdnimg.cn/20200317002221427.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3OTY1MDE4,size_16,color_FFFFFF,t_70) ### 程式碼實現 開始建立我們的野怪類,就叫做Hero吧,它的組成部分有技能類Skill,裝備類Armor 和武器類Weapon 。 建立Skill、Armor和Weapon類 ```java public class Skill { private String skillName; public Skill(String skillName) { this.skillName = skillName; } @Override public String toString() { return "Skill{" + "skillName='" + skillName + '\'' + '}'; } } public class Armor { private String armorName; public Armor(String armorName) { this.armorName = armorName; } @Override public String toString() { return "Armor{" + "armorName='" + armorName + '\'' + '}'; } } public class Weapon { private String weaponName; public Weapon(String weaponName) { this.weaponName = weaponName; } @Override public String toString() { return "Weapon{" + "weaponName='" + weaponName + '\'' + '}'; } } ``` 建立Hero類,在該類中,我們使用靜態內部類的方式構建了Builder類,就是我們使用Builder類幫助我們建立物件。 忘了啥是內部類的,可以移駕下面這篇複習下。 [Java內部類超詳細總結(含程式碼示例)](https://blog.csdn.net/m0_37965018/article/details/98457370) ```java public class Hero { private final String name; private final Skill skill; private final Armor armor; private final Weapon weapon; private Hero(Builder builder){ this.name = builder.name; this.skill = builder.skill; this.armor = builder.armor; this.weapon = builder.weapon; } @Override public String toString() { return "Hero{" + "name='" + name + '\'' + ", skill=" + skill + ", armor=" + armor + ", weapon=" + weapon + '}'; } public static class Builder{ private final String name; private Skill skill; private Armor armor; private Weapon weapon; public Builder(String name){ this.name = name; } public Builder withSkill(Skill skill){ this.skill = skill; return this; } public Builder withArmor(Armor armor){ this.armor = armor; return this; } public Builder withWeapon(Weapon weapon){ this.weapon = weapon; return this; } public Hero build(){ return new Hero(this); } } } ``` 好了,我們的builder模式的核心程式碼已經晚了,其實關鍵的就是Builder類,我們建立複雜物件就是通過Builder類封裝了建立的細節,同時,Builder提供了一些公共方法,可以定製這些複雜物件的建立過程。 新建個測試類AppMain,測試一把。 ```java public class AppMain { public static void main(String[] args) { Hero hero = new Hero.Builder("納什男爵") .withSkill(new Skill("飛龍在天")) .withArmor(new Armor("亢龍鎧甲")) .withWeapon(new Weapon("唾沫星子")) .build(); System.out.println(hero); } } ``` 結果如下: ```bash Hero{name='納什男爵', skill=Skill{skillName='飛龍在天'}, armor=Armor{armorName='亢龍鎧甲'}, weapon=Weapon{weaponName='唾沫星子'}} ``` 當然了,這裡也可以建立個“四鳥”,“河蟹”之類的。總之,你要打十個,麼有問題啊,我們給你builder十個就好了,而且是不重樣的。 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200317002709336.gif) ### Builder模式在原始碼中的應用 **StringBuilder** 其實我們熟知的StringBuilder就是builder模式的典型實現。我們平時使用基本都是這樣: ```java StringBuilder sb = new StringBuilder(); sb.append(123).append('a') .append(1.23) .append(true) .append("hhhh"); ``` 看著就很平常,soeasy的感覺,其實可以看到它能新增不同的資料型別進去,對應建造者模式中的各個部分,通過append方法的不同組合構建出了不同的StringBuilder物件。 看下原始碼: ```java ...... @Override public StringBuilder append(boolean b) { super.append(b); return this; } @Override public StringBuilder append(char c) { super.append(c); return this; } ...... ``` 上面列舉了兩個過載方法,可以看到其實是呼叫了父類的過載方法,父類是AbstractStringBuilder ```java // 這裡只列舉這一個父類的方法 public AbstractStringBuilder append(boolean b) { if (b) { ensureCapacityInternal(count + 4); value[count++] = 't'; value[count++] = 'r'; value[count++] = 'u'; value[count++] = 'e'; } else { ensureCapacityInternal(count + 5); value[count++] = 'f'; value[count++] = 'a'; value[count++] = 'l'; value[count++] = 's'; value[count++] = 'e'; } return this; } ``` **Mybatis中的builder模式** Mybatis中的SqlSessionFactoryBuilder、XMLMapperBuilder、XMLStatementBuilder等都使用了builder模式。 這裡簡單看下SqlSessionFactoryBuilder ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200316234910657.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM3OTY1MDE4,size_16,color_FFFFFF,t_70) 所有的build過載方法都在構建SqlSessionFactory 物件。只是可以根據需要呼叫不同的方法,傳入不同的引數,就可以構建出特性不同的SqlSessionFactory 。 看下其中一個build方法的原始碼 ```java ...... public SqlSessionFactory build(Reader reader, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException var13) { } } return var5; } ...... ``` 而且可以看到,這個方法中又使用了一個XMLConfigBuilder 。 ### Builder模式的使用場景 下面再總結一下builder模式的使用場景吧。 - 建立複雜物件的演算法應該獨立於組成物件的部件及其組裝方式。 - 構造物件的過程允許所構造的物件的不同表示。 ### 設計模式往期回顧 [Java面試必備:手寫單例模式](https://blog.csdn.net/m0_37965018/article/details/93791567) [工廠模式超詳解(程式碼示例)](https://blog.csdn.net/m0_37965018/article/details/103152585) [設計模式之原型模式](https://blog.csdn.net/m0_37965018/article/details/103192446) [設計模式之代理模式](https://blog.csdn.net/m0_37965018/article/details/103229235) [設計模式之委派模式,大名鼎鼎的Spring都在用](https://blog.csdn.net/m0_37965018/article/details/104867063) >公眾號:二營長的筆記 >免費領資料:公眾號內回覆“二