創建型模式:建造者模式

什么是建造者模式

建造者模式(Builder Pattern)是一種創建型設計模式,它將一個復雜對象的構建過程與其表示分離,使得同樣的構建過程可以創建不同的表示。簡單來說,建造者模式允許您一步一步創建復雜對象,而不是一次性傳入所有參數。

建造者模式特別適合那些需要多個步驟構建、有多個可選參數或者構建過程中需要遵循特定順序的對象創建場景。

為什么需要建造者模式

假設我們有一個具有十多個屬性的Computer類,其中一些屬性是必需的,而其他屬性是可選的。使用傳統的構造函數或JavaBean模式創建對象會帶來以下問題:

  1. 伸縮構造函數問題:需要編寫多個構造函數來處理不同的參數組合
  2. 可讀性差:當參數較多時,構造函數調用難以理解
  3. 狀態不一致:使用setter方法時,對象可能處于不完整狀態
  4. 線程安全問題:構建過程中對象狀態可能發生變化

建造者模式優雅地解決了這些問題。

建造者模式的核心實現

// 產品類
public class House {// 必要參數private final String foundation;private final String structure;private final String roof;// 可選參數private final String interior;private final String exterior;private final boolean hasGarage;private final boolean hasSwimmingPool;private final boolean hasGarden;// 私有構造函數,只能通過Builder訪問private House(Builder builder) {this.foundation = builder.foundation;this.structure = builder.structure;this.roof = builder.roof;this.interior = builder.interior;this.exterior = builder.exterior;this.hasGarage = builder.hasGarage;this.hasSwimmingPool = builder.hasSwimmingPool;this.hasGarden = builder.hasGarden;}// Getterspublic String getFoundation() { return foundation; }public String getStructure() { return structure; }public String getRoof() { return roof; }public String getInterior() { return interior; }public String getExterior() { return exterior; }public boolean hasGarage() { return hasGarage; }public boolean hasSwimmingPool() { return hasSwimmingPool; }public boolean hasGarden() { return hasGarden; }@Overridepublic String toString() {return "House with " + foundation + " foundation, " + structure + " structure, " + roof + " roof, " + interior + " interior, " +exterior + " exterior. " +(hasGarage ? "Has garage. " : "") +(hasSwimmingPool ? "Has swimming pool. " : "") +(hasGarden ? "Has garden." : "");}// 靜態內部Builder類public static class Builder {// 必要參數private final String foundation;private final String structure;private final String roof;// 可選參數 - 設置默認值private String interior = "普通裝修";private String exterior = "標準外墻";private boolean hasGarage = false;private boolean hasSwimmingPool = false;private boolean hasGarden = false;// 必要參數通過構造器強制傳入public Builder(String foundation, String structure, String roof) {this.foundation = foundation;this.structure = structure;this.roof = roof;}// 可選參數通過具有鏈式調用的setter方法設置public Builder interior(String interior) {this.interior = interior;return this;}public Builder exterior(String exterior) {this.exterior = exterior;return this;}public Builder garage(boolean hasGarage) {this.hasGarage = hasGarage;return this;}public Builder swimmingPool(boolean hasSwimmingPool) {this.hasSwimmingPool = hasSwimmingPool;return this;}public Builder garden(boolean hasGarden) {this.hasGarden = hasGarden;return this;}// build方法創建最終對象public House build() {// 可以在這里添加構建驗證邏輯return new House(this);}}
}

建造者模式的關鍵點

  1. 產品類:復雜對象,通常是不可變的
  2. Builder類:負責定義產品創建步驟的接口
  3. 鏈式調用:Builder方法返回Builder自身,支持流式API
  4. 最終構建方法:完成產品創建的方法(如build()
  5. 參數分離:將必選參數和可選參數明確區分

使用建造者模式

public class BuilderPatternDemo {public static void main(String[] args) {// 使用Builder創建對象House simpleHouse = new House.Builder("混凝土基礎", "磚墻結構", "平頂屋頂").build();System.out.println("簡易房屋:");System.out.println(simpleHouse);// 使用Builder創建更復雜的對象,設置可選參數House luxuryHouse = new House.Builder("深層鋼筋混凝土基礎", "鋼筋混凝土框架", "坡頂屋頂").interior("豪華裝修").exterior("大理石外墻").garage(true).swimmingPool(true).garden(true).build();System.out.println("\n豪華房屋:");System.out.println(luxuryHouse);}
}

運行結果

簡易房屋:
House with 混凝土基礎 foundation, 磚墻結構 structure, 平頂屋頂 roof, 普通裝修 interior, 標準外墻 exterior. 豪華房屋:
House with 深層鋼筋混凝土基礎 foundation, 鋼筋混凝土框架 structure, 坡頂屋頂 roof, 豪華裝修 interior, 大理石外墻 exterior. Has garage. Has swimming pool. Has garden.

建造者模式的高級版本:帶Director的建造者模式

在某些情況下,我們可能需要一個額外的Director類來封裝構建過程,特別是當構建過程復雜或需要被重用時。

// 抽象Builder接口
public interface HouseBuilder {void buildFoundation();void buildStructure();void buildRoof();void buildInterior();void buildExterior();void buildGarage();void buildSwimmingPool();void buildGarden();House getResult();
}// 具體Builder實現
public class StandardHouseBuilder implements HouseBuilder {private String foundation;private String structure;private String roof;private String interior;private String exterior;private boolean hasGarage;private boolean hasSwimmingPool;private boolean hasGarden;@Overridepublic void buildFoundation() {this.foundation = "標準混凝土基礎";}@Overridepublic void buildStructure() {this.structure = "磚混結構";}@Overridepublic void buildRoof() {this.roof = "普通瓦片屋頂";}@Overridepublic void buildInterior() {this.interior = "基礎裝修";}@Overridepublic void buildExterior() {this.exterior = "普通外墻";}@Overridepublic void buildGarage() {this.hasGarage = false;}@Overridepublic void buildSwimmingPool() {this.hasSwimmingPool = false;}@Overridepublic void buildGarden() {this.hasGarden = false;}@Overridepublic House getResult() {return new House(foundation, structure, roof, interior, exterior, hasGarage, hasSwimmingPool, hasGarden);}
}// 另一個具體Builder實現
public class LuxuryHouseBuilder implements HouseBuilder {private String foundation;private String structure;private String roof;private String interior;private String exterior;private boolean hasGarage;private boolean hasSwimmingPool;private boolean hasGarden;@Overridepublic void buildFoundation() {this.foundation = "深層鋼筋混凝土基礎";}// 其他方法實現...@Overridepublic House getResult() {return new House(foundation, structure, roof, interior, exterior, hasGarage, hasSwimmingPool, hasGarden);}
}// Director類
public class HouseDirector {private HouseBuilder builder;public HouseDirector(HouseBuilder builder) {this.builder = builder;}// 更換Builderpublic void changeBuilder(HouseBuilder builder) {this.builder = builder;}// 構建最小可居住房屋public void constructMinimalHouse() {builder.buildFoundation();builder.buildStructure();builder.buildRoof();builder.buildInterior();builder.buildExterior();}// 構建完整功能房屋public void constructFullFeaturedHouse() {builder.buildFoundation();builder.buildStructure();builder.buildRoof();builder.buildInterior();builder.buildExterior();builder.buildGarage();builder.buildSwimmingPool();builder.buildGarden();}
}

實際應用示例:流暢的API構建器

下面通過一個SQL查詢構建器的例子來展示建造者模式在實際API設計中的應用:

// SQL查詢構建器
public class SQLQueryBuilder {private String table;private List<String> columns = new ArrayList<>();private List<String> conditions = new ArrayList<>();private List<String> orderBy = new ArrayList<>();private Integer limit;private Integer offset;public SQLQueryBuilder from(String table) {this.table = table;return this;}public SQLQueryBuilder select(String... columns) {if (columns.length == 0) {this.columns.add("*");} else {this.columns.addAll(Arrays.asList(columns));}return this;}public SQLQueryBuilder where(String condition) {this.conditions.add(condition);return this;}public SQLQueryBuilder orderBy(String column, boolean ascending) {this.orderBy.add(column + (ascending ? " ASC" : " DESC"));return this;}public SQLQueryBuilder limit(int limit) {this.limit = limit;return this;}public SQLQueryBuilder offset(int offset) {this.offset = offset;return this;}public String build() {// 驗證必要參數if (table == null) {throw new IllegalStateException("表名未指定");}StringBuilder query = new StringBuilder();query.append("SELECT ");// 添加列if (columns.isEmpty()) {query.append("*");} else {query.append(String.join(", ", columns));}// 添加表query.append(" FROM ").append(table);// 添加條件if (!conditions.isEmpty()) {query.append(" WHERE ");query.append(String.join(" AND ", conditions));}// 添加排序if (!orderBy.isEmpty()) {query.append(" ORDER BY ");query.append(String.join(", ", orderBy));}// 添加限制if (limit != null) {query.append(" LIMIT ").append(limit);}// 添加偏移if (offset != null) {query.append(" OFFSET ").append(offset);}return query.toString();}
}

SQL構建器的使用示例

public class SQLBuilderDemo {public static void main(String[] args) {// 創建簡單查詢String simpleQuery = new SQLQueryBuilder().from("users").select("id", "name", "email").build();System.out.println("簡單查詢:");System.out.println(simpleQuery);// 創建復雜查詢String complexQuery = new SQLQueryBuilder().select("u.id", "u.name", "COUNT(o.id) as order_count").from("users u LEFT JOIN orders o ON u.id = o.user_id").where("u.status = 'active'").where("o.create_time > '2023-01-01'").orderBy("order_count", false).limit(10).offset(20).build();System.out.println("\n復雜查詢:");System.out.println(complexQuery);}
}

建造者模式的常見應用場景

  1. 文檔生成器:構建HTML、PDF、Word文檔
  2. 復雜對象構建:具有多個配置選項的對象
  3. 不可變對象創建:需要一次性設置所有屬性的對象
  4. 流暢的API設計:提供鏈式調用的接口
  5. 配置構建:應用程序配置、網絡請求配置等
  6. 測試數據構建:創建測試用例數據
  7. 復雜GUI創建:組裝UI組件和布局

真實世界中的建造者模式應用

  1. Java中的StringBuilder/StringBuffer:字符串構建
  2. Lombok的@Builder注解:自動生成Builder代碼
  3. Spring框架的UriComponentsBuilder:構建URI
  4. Apache Camel的RouteBuilder:構建路由規則
  5. Retrofit的RequestBuilder:構建HTTP請求
  6. OkHttp的Request.Builder:構建HTTP請求

建造者模式的優點

  1. 參數控制:分離必選參數和可選參數
  2. 構建過程封裝:隱藏復雜構建過程的細節
  3. 可讀性:創建對象的代碼更具可讀性
  4. 靈活性:同一構建過程可創建不同表示
  5. 不變性:可以創建不可變對象
  6. 參數驗證:可在構建時檢查參數有效性

建造者模式的缺點

  1. 代碼量增加:需要創建額外的Builder類
  2. 復雜度:對于簡單對象,可能過于復雜
  3. 性能開銷:相比直接構造,略有性能損失
  4. 維護成本:當產品類變化時,需要同步修改Builder

建造者模式與工廠模式的區別

雖然建造者模式和工廠模式都是創建型模式,但它們有明顯區別:

  • 工廠模式關注對象創建種類(what)
  • 建造者模式關注復雜對象構建過程(how)

工廠用于創建整個對象,而建造者關注對象各個部分的創建過程和裝配順序。

建造者模式變體:自引用泛型流暢接口

這種高級變體使用泛型保持鏈式調用時的類型安全:

public class Email {// 郵件屬性private final String from;private final List<String> to;private final String subject;private final String body;private final List<String> attachments;private Email(Builder builder) {this.from = builder.from;this.to = new ArrayList<>(builder.to);this.subject = builder.subject;this.body = builder.body;this.attachments = new ArrayList<>(builder.attachments);}// 泛型Builder基類public static class Builder<T extends Builder<T>> {private String from;private List<String> to = new ArrayList<>();private String subject = "";private String body = "";private List<String> attachments = new ArrayList<>();// 自引用泛型類型轉換@SuppressWarnings("unchecked")protected final T self() {return (T) this;}public T from(String from) {this.from = from;return self();}public T to(String address) {this.to.add(address);return self();}public T subject(String subject) {this.subject = subject;return self();}public T body(String body) {this.body = body;return self();}public T attachment(String attachment) {this.attachments.add(attachment);return self();}public Email build() {validateRequiredFields();return new Email(this);}private void validateRequiredFields() {if (from == null || from.isEmpty()) {throw new IllegalStateException("發件人不能為空");}if (to.isEmpty()) {throw new IllegalStateException("收件人不能為空");}}}// 具體Builderpublic static class EmailBuilder extends Builder<EmailBuilder> {// 不需要額外代碼,因為基類已實現了所有功能}// 便捷的靜態工廠方法public static EmailBuilder builder() {return new EmailBuilder();}@Overridepublic String toString() {return "Email{" +"from='" + from + '\'' +", to=" + to +", subject='" + subject + '\'' +", body='" + body + '\'' +", attachments=" + attachments +'}';}
}

建造者模式最佳實踐

  1. 使用場景選擇:對象參數較多或構建過程復雜時使用
  2. 參數驗證:在build()方法中進行完整性校驗
  3. 必選與可選分離:通過構造函數傳入必選參數
  4. 不可變對象:建造者模式是創建不可變對象的好方法
  5. 鏈式API設計:返回this支持流暢接口
  6. 避免過早優化:簡單對象不需要建造者模式
  7. 使用嵌套靜態類:使Builder成為產品的靜態內部類

建造者模式小結

建造者模式是一種強大的創建型設計模式,它通過分離復雜對象的構建過程與表示,使得創建過程更加靈活和可控。它特別適合于有多個參數選項的復雜對象創建,可以提高代碼的可讀性和可維護性。

在實際應用中,許多現代API和框架都采用了建造者模式的變體來提供流暢的接口。掌握這一模式有助于我們設計出更易用、更靈活的API,并解決對象創建中的復雜性問題。

無論是創建不可變對象,還是設計流暢的API,建造者模式都是一種值得掌握的重要設計模式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/77857.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/77857.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/77857.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux `init 5` 相關命令的完整使用指南

Linux init 5 相關命令的完整使用指南—目錄 一、init 系統簡介二、init 5 的含義與作用三、不同 Init 系統下的 init 5 行為1. SysVinit&#xff08;如 CentOS 6、Debian 7&#xff09;2. systemd&#xff08;如 CentOS 7、Ubuntu 16.04&#xff09;3. Upstart&#xff08;如 …

RabbitMQ常見面試題回答重點

文章目錄 什么是消息隊列&#xff1f;為什么需要消息隊列消息隊列的模型消息隊列常見名詞如何保證消息不丟失&#xff1f;&#xff08;可靠性&#xff09;如何保證消息不重復/業務冪等性如何保證消息有序性如何處理消息堆積消息隊列設計為推送還是拉取 / 推拉模式優點無法路由的…

欣佰特攜數十款機器人相關前沿產品,亮相第二屆人形機器人和具身智能行業盛會

2025年4月15日至16日&#xff0c;備受關注的第二屆中國人形機器人與具身智能產業大會已在北京成功舉行。作為國內前沿科技及產品服務領域的重要參與者&#xff0c;欣佰特科技攜眾多前沿產品精彩亮相&#xff0c;全方位展示了其在人形機器人與具身智能領域的創新產品。 在本次大…

Docker安裝 (centos)

1.安裝依賴包&#xff1a; sudo yum install -y yum-utils device-mapper-persistent-data lvm2 2.刪除已有的 Docker 倉庫文件&#xff08;如果有&#xff09;&#xff1a; sudo rm -f /etc/yum.repos.d/docker-ce.repo 3.添加阿里云的 Docker 倉庫&#xff1a; sudo yum…

Vue接口平臺學習十——接口用例頁面2

效果圖及簡單說明 左邊選擇用例&#xff0c;右側就顯示該用例的詳細信息。 使用el-collapse折疊組件&#xff0c;將請求到的用例詳情數據展示到頁面中。 所有數據內容&#xff0c;綁定到caseData中 // 頁面綁定的用例編輯數據 const caseData reactive({title: "",…

服務器數據遷移指南

服務器數據遷移是將數據從一臺服務器轉移到另一臺服務器的過程&#xff0c;可能是為了硬件升級、云遷移、數據中心搬遷或服務整合。 以下是數據遷移的關鍵步驟和注意事項&#xff1a; 遷移前準備 制定遷移計劃 確定遷移范圍(全量/增量) 評估數據量和網絡帶寬 制定時間表和回…

25.解決中醫知識問答刪除歷史對話功能后端處理請求時拋出異常

ChatTest.vue:176 DELETE http://localhost:8080/api/chat/conversations/20 500 (Internal Server Error) deleteConversation ChatTest.vue:176 onClick ChatTest.vue:22 ChatTest.vue:185 刪除失敗 AxiosError {message: Request failed with status code 500, name: Axio…

記錄seatunnel排查重復數據的案例分析

文章目錄 背景分析檢查現象檢查B集群是否有異常&#xff0c;導致重復消費的分析同步任務 修復問題發現flink job 一直報異常修復問題 背景 使用seatunnel 同步數據從A 集群kafka 同步到B集群kafka,現象是發現兩邊數據不一致&#xff0c;每天10w級別會多幾十條數據 分析 檢查…

VSCode遠程圖形化GDB

VSCode遠程圖形化GDB 摘要一、安裝VSCode1、使用.exe安裝包安裝VSCode2、VSCode 插件安裝3、VSCode建立遠程連接 二、core dump找bug1、開啟core文件2、永久生效的方法3、編寫測試程序4、運行結果5、查看core段錯誤位置6、在程序中開啟core dump并二者core文件大小 三、gdbserv…

Android 中實現 GIF 圖片動畫

在 Android 中&#xff0c;ImageView 從 Android 9.0&#xff08;API 級別 28&#xff09; 開始原生支持 GIF 動畫&#xff0c;通過 AnimatedImageDrawable 類實現。在之前的版本中&#xff0c;ImageView 并不支持直接播放 GIF 動畫&#xff0c;只能顯示 GIF 的第一幀。 一、 …

【c語言】指針進階

目錄 1.字符指針 2.指針數組 3.數組指針 3.1 數組指針的定義 3.2 數組指針的使用 4.數組參數&#xff0c;指針參數 4.1 一維數組傳參 4.2 二維數組傳參 4.3 一級指針傳參 4.4 二級指針傳參 5.函數指針 6.函數指針數組 6.1函數指針數組的定義 6.2 函數指針數組…

極狐GitLab 項目 API 的速率限制如何設置?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 項目 API 的速率限制 (BASIC SELF) 引入于 15.10 版本&#xff0c;功能標志為rate_limit_for_unauthenticated_projects_api_…

【xlog日志文件】怎么刪除里面包含某些字符串的行(使用excel)

將log日志,復制到單獨一行 B列&#xff08;可能一行很長&#xff0c;所以將整合后的放在A列&#xff09; 使用公式可以篩選出 包含某些字符串的行 為true&#xff0c;將這些行直接刪除 IF(COUNT(FIND("MediaMuxterThreadRussia",B2,1))>0,"包含",&quo…

STM32提高篇: CAN通訊

STM32提高篇: CAN通訊 一.CAN通訊介紹1.物理層2.協議層二.STM32CAN外設1.CAN控制器的3種工作模式2.CAN控制器的3種測試模式3.功能框圖三.CAN的寄存器介紹1.環回靜默模式測試2.雙擊互發測試四.CAN的HAL代碼解讀一.CAN通訊介紹 CAN(Controller Area Network 控制器局域網,簡稱…

Java寫數據結構:棧

1.概念&#xff1a; 一種特殊的線性表&#xff0c;其只允許在固定的一端進行插入和刪除元素操作。進行數據插入和刪除操作的一端稱為棧頂&#xff0c;另一端稱為棧底。棧中的數據元素遵守后進先出LIFO&#xff08;Last In First Out&#xff09;的原則。 壓棧&#xff1a;棧的插…

單頁面應用的特點,什么是路由,VueRouter的下載,安裝和使用,路由的封裝抽離,聲明式導航的介紹和使用

文章目錄 一.什么是單頁面應用?二.什么是路由?生活中的路由和Vue中的路由 三.VueRouter(重點)0.引出1.介紹2.下載與使用(5個基本步驟2個核心步驟)2.1 五個基本步驟2.2 兩個核心步驟 四.路由的封裝抽離五.聲明式導航1.導航鏈接特點一:能跳轉特點二:能高亮 2.兩個高亮類名2.1.區…

【C++】模板2.0

最近學習了一些模板的知識&#xff0c;速寫本博客作為學習筆記&#xff0c;若有興趣&#xff0c;歡迎垂閱讀&#xff01; 1.非類型模板參數 模板參數分類類型形參與非類型形參。 類型形參即&#xff1a;出現在模板參數列表中&#xff0c;跟在class或者typename之類的參數類型名…

目標檢測中的損失函數(二) | BIoU RIoU α-IoU

BIoU來自發表在2018年CVPR上的文章&#xff1a;《Improving Object Localization With Fitness NMS and Bounded IoU Loss》 論文針對現有目標檢測方法只關注“足夠好”的定位&#xff0c;而非“最優”的框&#xff0c;提出了一種考慮定位質量的NMS策略和BIoU loss。 這里不贅…

如何在 Amazon EC2 上部署 Java(Spring Boot 版)

讓我們學習如何將 Java Spring Boot Web 服務器部署到 Amazon EC2。每月只需 3 美元。 使用 Azure&#xff0c;您可能不知道要花費多少錢。 Spring Boot 項目示例 在本教程中&#xff0c;我們將重點介紹如何將 Java Spring Boot 服務器部署到 Amazon EC2&#xff0c;因此我們不…

Git常用命令分類匯總

Git常用命令分類匯總 一、基礎操作 初始化倉庫git init添加文件到暫存區git add file_name # 添加單個文件 git add . # 添加所有修改提交更改git commit -m "提交描述"查看倉庫狀態git status二、分支管理 創建/切換分支git branch branch_name …