04-04-設計模式 建造者模式
阿新 • • 發佈:2022-05-27
需求
需要建造房子, 過程為: 打樁->砌牆->封頂
房子有各式各樣的, 比如普通房, 高樓, 別墅, 過程雖然一樣, 要是要求不要相同的
傳統方式解決
新建抽象類
package com.dance.design.designmodel.buildmodel; import lombok.Builder; import lombok.Data; /** * 抽象房子 */ public abstract class AbsHouse { /** * 建造 */ public void build(){ buildBasic(); buildWalls(); buildTop(); } /** * 打樁 * @param basic 地基 */ public abstract void buildBasic(); /** * 砌牆 * @param walls 牆 */ public abstract void buildWalls(); /** * 封頂 * @param top 頂部顏色 */ public abstract void buildTop(); }
新建實現類
package com.dance.design.designmodel.buildmodel; public class CommonHouse extends AbsHouse{ @Override public void buildBasic() { System.out.println("建造普通房子的地基"); } @Override public void buildWalls() { System.out.println("建造普通房子的牆"); } @Override public void buildTop() { System.out.println("建造普通房子的頂"); } }
新建客戶端
package com.dance.design.designmodel.buildmodel; public class Client { public static void main(String[] args) { AbsHouse absHouse = new CommonHouse(); absHouse.build(); } }
傳統方式的問題分析
- 優點是比較好理解, 簡單易操作
- 設計的程式結構, 過於簡單, 沒有設計快取層物件, 程式的擴充套件和維護不好, 也就是說這種設計方案把產品(房子)和建立的過程封裝在一起,耦合性增強了
- 解決方案: 將產品和產品的建造過程解耦 => 建造者模式
建造者模式
基本介紹
- 建造者模式又叫生成器模式,是一種物件構建模式,他可以將複雜的物件的建造過程抽象出來(抽象類別), 使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件
- 建造者模式是一步一步建立一個複雜的物件, 它允許使用者只通過指定複雜物件的型別和內容就可以構建他們,使用者不需要知道內部的具體構建細節
建造者模式的四個角色
- Product(產品角色) : 一個具體的產品物件
- Builder(抽象建造者) : 建立一個Product物件的各個部件指定的 介面/抽象類
- ConcreateBuilder(具體建造者): 實現介面, 構建和裝配各個部件
- Director(指揮者): 構建一個使用Builder介面的物件, 它主要是用於建立一個複雜的物件, 它主要有兩個作用
- 隔離了客戶與物件的生產過程
- 負責控制產品物件的生產過程
建造者模式原理類圖
使用建造者模式改進需求
類圖
- 抽象建造者組合了產品
- 具體建造者實現了抽象建造者的抽象方法
- 建造指揮者聚合了抽象建造者, 提供設定方法
- 客戶端(呼叫方)依賴建造指揮者
實現
package com.dance.design.designmodel.buildmodel.build; import lombok.Data; import lombok.Setter; public class Client2 { public static void main(String[] args) { // 建造普通的房子 // 建立指揮者 傳入建造的具體實現 HouseDirector houseDirector = new HouseDirector(new CommonHouse()); // 建造 House house = houseDirector.constructHouse(); // 設定建造高樓大廈 houseDirector.setHouseBuilder(new HighHouse()); // 建造 house = houseDirector.constructHouse(); } } /** * 產品 */ @Data class House{ private String basic; private String wall; private String roofed; } /** * 抽象建造者 */ abstract class HouseBuilder{ // 組合產品 protected House house = new House(); public abstract void buildBasic(); public abstract void buildWalls(); public abstract void buildRoofed(); /** * 建造 * @return 房子 */ public House buildHouse(){ return house; } } /** * 普通房子 */ class CommonHouse extends HouseBuilder{ @Override public void buildBasic() { System.out.println("打地基5m"); } @Override public void buildWalls() { System.out.println("砌牆10m"); } @Override public void buildRoofed() { System.out.println("封頂10m2"); } } /** * 高樓大廈 */ class HighHouse extends HouseBuilder{ @Override public void buildBasic() { System.out.println("打地基20m"); } @Override public void buildWalls() { System.out.println("砌牆200m"); } @Override public void buildRoofed() { System.out.println("封頂200m2"); } } /** * 指揮者 */ class HouseDirector{ @Setter private HouseBuilder houseBuilder = null; /** * 傳入具體的 要建造的房子 * @param houseBuilder */ public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } // 建造流程交給指揮者 public House constructHouse(){ houseBuilder.buildBasic(); houseBuilder.buildWalls(); houseBuilder.buildRoofed(); return houseBuilder.buildHouse(); } }
原始碼剖析
JDK中的建造者模式
StringBuilder 從名字就能看出來,這時一個建造者
- CharSequence : 字元序列 屬於產品 因為要建立的為字串
- Appendable : 功能介面, 支援追加功能append介面
- AbstractStringBuilder : 抽象建造者
- 實現了CharSequence(介面)
- 實現了Appendable(新增功能介面)
- 具體建造者
- 具體實現有StringBuffer 和 StringBuilder
- StringBuilder : 具體建造者, 兼職指揮者 繼承了 抽象建造者
- 使用的時候直接使用具體建造者
建造者模式的注意事項和細節
- 客戶端(使用程式)不必知道產品內部組成的細節, 將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件
- 每一個具體建造者都相對獨立, 而與其他的具體建造者無關,因此可以很方便的替換具體建造者或增加新的具體建造者, 使用者使用不同的具體建造者即可得到不同的產品物件
- 可以更加精細的控制產品的建立過程, 將複雜產品的建立步驟分解在不同的方法中, 使得建立過程更加清晰,也更方便使用程式來控制建立過程
- 增加新的具體建造者無需修改原有類庫的程式碼, 指揮者針對抽象建造者程式設計, 系統擴充套件方便, 符合開閉原則
- 建造者模式所建立的產品一般具有較多的共同點, 其組成部分相似,如果產品之間差異性很大,則不適合使用建造者模式,因為此使用範圍受到一定的限制
- 如果產品內部的變化複雜, 可能會導致需要定義很多具體建造者類來實現這種變化, 導致系統變得很龐大, 因此這種請款下, 要考慮是否使用建造者模式
- 抽象工廠模式 VS 建造者模式
- 抽象工廠模式實現對產品家族的建立, 一個產品家族是這樣的一系列產品, 具有不同分類維度的產品組合, 採用抽象工廠模式不關係建造過程,只關心什麼產品由什麼工廠產生即可, 而建造者模式則是要求按照指定的藍圖建造產品,他的主要目的是通過組裝零配件而生產一個新產品
- 其實就是關注粒度不同, 工廠模式只關注由那個工廠生產, 建造者需要關心建造過程
擴充套件: 為什麼StringBuilder不是執行緒安全的?
一般我們面試的時候會問?
面試官: StringBuilder和StringBuffer有什麼區別?
我: StringBuilder執行緒不安全, StringBuffer是執行緒安全的
面試官: StringBuilder 哪裡不安全? StringBuffer為啥安全?
我 : ........
沒得聊了
接下來說一下
我們先分析Stringbuilder的append方法
從原始碼可以看出呼叫的是父類的append方法
他的父類就是抽象建造者 AbstractStringBuilder ,從程式碼可以看出在進行count(統計char陣列的長度)累加的時候 是一個+=的操作符,這個操作就不是一個原子操作, 所以會導致多執行緒操作不安全
那為什麼StringBuffer是執行緒安全的呢?
從原始碼可以看出 StringBuffer的append方法添加了synchronized修飾, 這個直接是物件鎖, 直接將方法加了鎖,導致了哪怕是多執行緒操作一個物件的append方法也要乖乖的等著另一個執行緒執行完成....