【設計模式】掌握建造者模式:如何優雅地解決復雜對象創建難題?

概述

將一個復雜對象的構建與表示分離,使得同樣的構建過程可以創建不同的表示。

  • 分離了部件的構造(由Builder來負責)和裝配(由Director負責)。 從而可以構造出復雜的對象。這個模式適用于:某個對象的構建過程復雜的情況。

  • 由于實現了構建和裝配的解耦。不同的構建器,相同的裝配,也可以做出不同的對象;相同的構建器,不同的裝配順序也可以做出不同的對象。也就是實現了構建算法、裝配算法的解耦,實現了更好的復用。

  • 建造者模式可以將部件和其組裝過程分開,一步一步創建一個復雜的對象。用戶只需要指定復雜對象的類型就可以得到該對象,而無須知道其內部的具體構造細節。

結構

建造者(Builder)模式包含如下角色:

  • 抽象建造者類(Builder):這個接口規定要實現復雜對象的那些部分的創建,并不涉及具體的部件對象的創建。

  • 具體建造者類(ConcreteBuilder):實現 Builder 接口,完成復雜產品的各個部件的具體創建方法。在構造過程完成后,提供產品的實例。

  • 產品類(Product):要創建的復雜對象。

  • 指揮者類(Director):調用具體建造者來創建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建。

類圖如下:

實例

創建共享單車

生產自行車是一個復雜的過程,它包含了車架,車座等組件的生產。而車架又有碳纖維,鋁合金等材質的,車座有橡膠,真皮等材質。對于自行車的生產就可以使用建造者模式。

這里Bike是產品,包含車架,車座等組件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具體的建造者;Director是指揮者。類圖如下:

具體的代碼如下:

//自行車類
public class Bike {private String frame;private String seat;public String getFrame() {return frame;}public void setFrame(String frame) {this.frame = frame;}public String getSeat() {return seat;}public void setSeat(String seat) {this.seat = seat;}
}// 抽象 builder 類
public abstract class Builder {protected Bike mBike = new Bike();public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();
}//摩拜單車Builder類
public class MobikeBuilder extends Builder {@Overridepublic void buildFrame() {mBike.setFrame("鋁合金車架");}@Overridepublic void buildSeat() {mBike.setSeat("真皮車座");}@Overridepublic Bike createBike() {return mBike;}
}//ofo單車Builder類
public class OfoBuilder extends Builder {@Overridepublic void buildFrame() {mBike.setFrame("碳纖維車架");}@Overridepublic void buildSeat() {mBike.setSeat("橡膠車座");}@Overridepublic Bike createBike() {return mBike;}
}//指揮者類
public class Director {private Builder mBuilder;public Director(Builder builder) {mBuilder = builder;}public Bike construct() {mBuilder.buildFrame();mBuilder.buildSeat();return mBuilder.createBike();}
}//測試類
public class Client {public static void main(String[] args) {showBike(new OfoBuilder());showBike(new MobikeBuilder());}private static void showBike(Builder builder) {Director director = new Director(builder);Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());}
}

注意:

上面示例是 Builder模式的常規用法,指揮者類 Director 在建造者模式中具有很重要的作用,它用于指導具體構建者如何構建產品,控制調用先后次序,并向調用者返回完整的產品類,但是有些情況下需要簡化系統結構,可以把指揮者類和抽象建造者進行結合

// 抽象 builder 類
public abstract class Builder {protected Bike mBike = new Bike();public abstract void buildFrame();public abstract void buildSeat();public abstract Bike createBike();public Bike construct() {this.buildFrame();this.BuildSeat();return this.createBike();}
}

說明:

這樣做確實簡化了系統結構,但同時也加重了抽象建造者類的職責,也不是太符合單一職責原則,如果construct() 過于復雜,建議還是封裝到 Director 中。

優缺點

優點:

  • 建造者模式的封裝性很好。使用建造者模式可以有效的封裝變化,在使用建造者模式的場景中,一般產品類和建造者類是比較穩定的,因此,將主要的業務邏輯封裝在指揮者類中對整體而言可以取得比較好的穩定性。

  • 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。

  • 可以更加精細地控制產品的創建過程 。將復雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。

  • 建造者模式很容易進行擴展。如果有新的需求,通過實現一個新的建造者類就可以完成,基本上不用修改之前已經測試通過的代碼,因此也就不會對原有功能引入風險。符合開閉原則。

缺點:

  • 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。

使用場景

建造者(Builder)模式創建的是復雜對象,其產品的各個部分經常面臨著劇烈的變化,但將它們組合在一起的算法卻相對穩定,所以它通常在以下場合使用。

  • 創建的對象較復雜,由多個部件構成,各部件面臨著復雜的變化,但構件間的建造順序是穩定的。

  • 創建復雜對象的算法獨立于該對象的組成部分以及它們的裝配方式,即產品的構建過程和最終的表示是獨立的。

模式擴展

建造者模式除了上面的用途外,在開發中還有一個常用的使用方式,就是當一個類構造器需要傳入很多參數時,如果創建這個類的實例,代碼可讀性會非常差,而且很容易引入錯誤,此時就可以利用建造者模式進行重構。

重構前代碼如下:

public class Phone {private String cpu;private String screen;private String memory;private String mainboard;public Phone(String cpu, String screen, String memory, String mainboard) {this.cpu = cpu;this.screen = screen;this.memory = memory;this.mainboard = mainboard;}public String getCpu() {return cpu;}public void setCpu(String cpu) {this.cpu = cpu;}public String getScreen() {return screen;}public void setScreen(String screen) {this.screen = screen;}public String getMemory() {return memory;}public void setMemory(String memory) {this.memory = memory;}public String getMainboard() {return mainboard;}public void setMainboard(String mainboard) {this.mainboard = mainboard;}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}
}public class Client {public static void main(String[] args) {//構建Phone對象Phone phone = new Phone("intel","三星屏幕","金士頓","華碩");System.out.println(phone);}
}

上面在客戶端代碼中構建Phone對象,傳遞了四個參數,如果參數更多呢?代碼的可讀性及使用的成本就是比較高。

重構后代碼:

public class Phone {private String cpu;private String screen;private String memory;private String mainboard;private Phone(Builder builder) {cpu = builder.cpu;screen = builder.screen;memory = builder.memory;mainboard = builder.mainboard;}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;public Builder() {}public Builder cpu(String val) {cpu = val;return this;}public Builder screen(String val) {screen = val;return this;}public Builder memory(String val) {memory = val;return this;}public Builder mainboard(String val) {mainboard = val;return this;}public Phone build() {return new Phone(this);}}@Overridepublic String toString() {return "Phone{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainboard='" + mainboard + '\'' +'}';}
}public class Client {public static void main(String[] args) {Phone phone = new Phone.Builder().cpu("intel").mainboard("華碩").memory("金士頓").screen("三星").build();System.out.println(phone);}
}

重構后的代碼在使用起來更方便,某種程度上也可以提高開發效率。從軟件設計上,對程序員的要求比較高。

創建者模式對比

工廠方法模式VS建造者模式

工廠方法模式注重的是整體對象的創建方式;而建造者模式注重的是部件構建的過程,意在通過一步一步地精確構造創建出一個復雜的對象。

我們舉個簡單例子來說明兩者的差異,如要制造一個超人,如果使用工廠方法模式,直接產生出來的就是一個力大無窮、能夠飛翔、內褲外穿的超人;而如果使用建造者模式,則需要組裝手、頭、腳、軀干等部分,然后再把內褲外穿,于是一個超人就誕生了。

抽象工廠模式VS建造者模式

抽象工廠模式實現對產品家族的創建,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,采用抽象工廠模式則是不需要關心構建過程,只關心什么產品由什么工廠生產即可。

建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品。

如果將抽象工廠模式看成汽車配件生產工廠,生產一個產品族的產品,那么建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以返回一輛完整的汽車。

往期推薦

  • 《SpringBoot》EasyExcel實現百萬數據的導入導出
  • 《SpringBoot》史上最全SpringBoot相關注解介紹
  • Spring框架IoC核心詳解
  • 萬字長文帶你窺探Spring中所有的擴展點
  • 如何實現一個通用的接口限流、防重、防抖機制
  • 萬字長文帶你深入Redis底層數據結構
  • volatile關鍵字最全原理剖析

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

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

相關文章

量子計算對區塊鏈技術的影響:革新與挑戰

量子計算對區塊鏈技術的影響:革新與挑戰 大家好,我是你們的技術伙伴Echo_Wish。今天我們來探討一個頗具前沿性的話題——量子計算對區塊鏈技術的影響。量子計算作為新一代計算技術,其強大的計算能力為各個領域帶來了革新。然而,量…

【Java代碼審計 | 第八篇】文件操作漏洞成因及防范

未經許可,不得轉載。 文章目錄 文件操作漏洞文件讀取漏洞基于 InputStream 的讀取基于 FileReader 的讀取 文件下載漏洞文件刪除漏洞防范 文件操作漏洞 分為文件讀取漏洞、文件下載漏洞與文件刪除漏洞。 文件讀取漏洞 在Java中,文件讀取通常有兩種常見…

與rkipc通信

rkipc的通信方式 在ipcweb中,程序是通過/var/tmp/rkipc和rkipc進行通信,并且網絡和客戶端的函數封裝在luckfox-pico/project/app/ipcweb/ipcweb-backend/src/socket_client文件夾中, client.cpp是客戶端命令 socket.cpp是網絡命令 編寫rkip…

NLP常見任務專題介紹(2)-多項選擇任務(MultipleChoice)訓練與推理模板

一、 使用 BigBird 進行多項選擇任務訓練與推理 本示例展示如何使用 BigBirdForMultipleChoice 訓練一個多項選擇模型,適用于考試答題、閱讀理解、常識推理等任務。 1?? 任務描述 目標:給定一個問題和多個選項,模型預測正確答案。 數據格式:輸入包含 (問題, 選項1, 選項…

【論文解讀】MODEST 透明物體 單目深度估計和分割 ICRA 2025

MODEST是一種用于透明物體的單目深度估計和分割的方法,來自ICRA 2025。 它通過單張RGB圖像作為輸入,能夠同時預測透明物體的深度圖和分割掩碼。 由深度圖生成點云數據,然后采用GraspNet生成抓取位姿,開展透明物體抓取實驗。 論文…

【網絡安全工程】任務11:路由器配置與靜態路由配置

目錄 一、概念 二、路由器配置 三、配置靜態路由CSDN 原創主頁:不羈https://blog.csdn.net/2303_76492156?typeblog 一、概念 1、路由器的作用:通過路由表進行數據的轉發。 2、交換機的作用:通過學習和識別 MAC 地址,依據 M…

深入理解隱式類型轉換:從原理到應用

C?持內置類型隱式類型轉換為類類型對象,需要有相關內置類型為參數的構造函數。 構造函數前?加explicit就不再?持隱式類型轉換。 類類型的對象之間也可以隱式轉換,需要相應的構造函數?持。 內置類型隱式類型轉換為類類型對象 在 C 中,如果…

垃圾收集算法與收集器

在 JVM 中,垃圾收集(Garbage Collection, GC)算法的核心目標是自動回收無用對象的內存,同時盡量減少對應用性能的影響。以下是 JVM 中主要垃圾收集算法的原理、流程及實際應用場景的詳細介紹: 一、標記-清除算法&#…

如何為服務設置合理的線程數

1. 首先,要確定最大線程數的限制因素。通常,線程數量受限于內存、CPU和操作系統限制。比如,每個線程都需要一定的棧內存,默認情況下Java線程的棧大小是1MB(64位系統可能更大),所以如果內存不足&…

內容中臺:元數據驅動管理新范式

元數據驅動智能管理中樞 現代企業內容管理正經歷從碎片化存儲向結構化治理的范式轉變,元數據驅動機制在此過程中展現出核心樞紐價值。通過構建多維屬性標簽體系,Baklib等內容中臺解決方案實現了對文本、音視頻等數字資產的精準定義,其動態分…

在mac中設置環境變量

步驟一:打開終端 步驟二:輸入printenv,查看當前已有的環境變量; 步驟三:輸入:nano ~/.zshrc 打開環境變量編輯頁面; 步驟四:輸入新的變量:export DEEPSEEK_API_KEY&qu…

擴散模型的算法原理及其在圖像生成領域的優勢與創新

目錄 一、引言 二、擴散模型的加噪過程 (一)前向擴散過程 (二)噪聲調度策略 三、擴散模型的去噪過程 (一)反向擴散過程 (二)去噪網絡架構 四、擴散模型的訓練和推理機制 &am…

技術領域,有許多優秀的博客和網站

在技術領域,有許多優秀的博客和網站為開發者、工程師和技術愛好者提供了豐富的學習資源和行業動態。以下是一些常用的技術博客和網站,涵蓋了編程、軟件開發、數據科學、人工智能、網絡安全等多個領域: 1. 綜合技術博客 1.1 Medium 網址: ht…

mysql經典試題共34題

1、準備數據 -- drop drop table if exists dept; drop table if exists emp; drop table if exists salgrade;-- CREATE CREATE TABLE dept (deptno int NOT NULL COMMENT 部門編號,dname varchar(14) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMM…

2025 - GDB 盲調筆記--調試 “無調試符號“ “無調試信息“ 的三方程序

環境: arm64-ubuntu 相關:strace、ltrace、readelf、patchelf、strings、ldd -v 1). 使用 gdb 啟動目標程序(不能直接用gdb啟動的,可以先單獨啟動,再 gdb attach 強制調試) DIR_APP/opt/test gdb --args env LANGUAGE LD_LIBRA…

OCPP擴展機制與自定義功能開發:協議靈活性設計與實踐 - 慧知開源充電樁平臺

OCPP擴展機制與自定義功能開發:協議靈活性設計與實踐 引言 OCPP作為開放協議,其核心價值在于平衡標準化與可擴展性。面對不同充電樁廠商的硬件差異、區域能源政策及定制化業務需求,OCPP通過**擴展點(Extension Points&#xff09…

【項目】nnUnetv2復現

作者提出一種nnUNet(no-new-Net)框架,基于原始的UNet(很小的修改),不去采用哪些新的結構,如相殘差連接、dense連接、注意力機制等花里胡哨的東西。相反的,把重心放在:預處理(resampling和normalization)、訓練(loss,optimizer設置、數據增廣)、推理(patch-based…

代碼隨想錄算法訓練營第八天|Leetcode 151.翻轉字符串里的單詞 卡碼網:55.右旋轉字符串 字符串總結 雙指針回顧

151.翻轉字符串里的單詞 建議:這道題目基本把 剛剛做過的字符串操作 都覆蓋了,不過就算知道解題思路,本題代碼并不容易寫,要多練一練。 題目鏈接/文章講解/視頻講解:代碼隨想錄 我們這道題的思路是,先將整…

【計算機網絡】計算機網絡的性能指標——時延、時延帶寬積、往返時延、信道利用率

計算機網絡的性能指標 導讀 大家好,很高興又和大家見面啦!!! 在上一篇內容中我們介紹了計算機網絡的三個性能指標——速率、帶寬和吞吐量。用大白話來說就是:網速、最高網速和實時網速。 相信大家看到這三個詞應該就…

Refreshtoken 前端 安全 前端安全方面

網絡安全 前端不需要過硬的網絡安全方面的知識,但是能夠了解大多數的網絡安全,并且可以進行簡單的防御前兩三個是需要的 介紹一下常見的安全問題,解決方式,和小的Demo,希望大家喜歡 網絡安全匯總 XSSCSRF點擊劫持SQL注入OS注入請求劫持DDOS 在我看來,前端可以了解并且防御前…