【软考设计模式】建造者模式:复杂对象的“分步组装“与代码填空精讲

📅 2026/7/9 7:10:34
【软考设计模式】建造者模式:复杂对象的“分步组装“与代码填空精讲
系列定位软考软件设计师 / 系统架构设计师 — 创建型模式专题第 4 讲考察分值上午题 1-2 分下午题 15 分常作为面向对象设计大题核心模式难度等级⭐⭐⭐⭐☆角色多、步骤多但逻辑清晰掌握后很稳一、考纲定位与模式定义1.1 考纲要求建造者模式在软考中属于创建型模式的中高难度内容。考察形式包括上午选择题判断场景描述所属模式识别建造者与工厂方法、抽象工厂的区别判断 Director 与 Builder 的职责划分下午设计题补全 Director 的construct()方法按步骤调用 builder 方法补全 ConcreteBuilder 的buildPart()和getResult()方法根据类图识别四个核心角色1.2 模式定义建造者模式将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示。核心意图当创建一个复杂对象需要很多步骤且这些步骤相对固定、但每个步骤的具体实现可以变化时把构建步骤算法和具体实现表示解耦。让客户端无需关心对象内部是怎么一步步拼出来的只需告诉 Director 按标准流程造一个再告诉 Builder 用哪种材料造。二、UML 类图与角色划分┌─────────────────────────────────────────────────────────┐ │ Client │ │ main() │ └───────────────────────┬─────────────────────────────────┘ │ uses ▼ ┌─────────────────────────────────────────────────────────┐ │ Director │ │ - builder: Builder │ │ construct() │ │ ├── builder.buildPartA() │ │ ├── builder.buildPartB() │ │ └── builder.buildPartC() │ └───────────────────────┬─────────────────────────────────┘ │ directs ▼ ┌─────────────────────────────────────────────────────────┐ │ interface │ │ Builder │ │ buildPartA() │ │ buildPartB() │ │ buildPartC() │ │ getResult(): Product │ └───────────────────────┬─────────────────────────────────┘ ┌────────────┴────────────┐ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────┐ │ ConcreteBuilderA │ │ ConcreteBuilderB │ │ buildPartA() │ │ buildPartA() │ │ buildPartB() │ │ buildPartB() │ │ buildPartC() │ │ buildPartC() │ │ getResult() │ │ getResult() │ └──────────┬──────────┘ └──────────┬──────────┘ │ creates │ creates ▼ ▼ ┌─────────────────────┐ ┌─────────────────────┐ │ ProductA │ │ ProductB │ │ 由PartAPartB │ │ (由PartAPartB │ │ PartC组装而成) │ │ PartC组装而成) │ └─────────────────────┘ └─────────────────────┘角色职责软考填空关键词Product产品被构建的复杂对象通常包含多个部件具体产品类名Builder抽象建造者声明创建产品各部件的接口声明getResult()返回最终产品interface/abstract classbuildPartXxx()ConcreteBuilder具体建造者实现 Builder 接口完成各部件的具体构建并返回组装好的产品implements/extendsreturn new Product()Director指挥者定义构建的步骤顺序算法调用 Builder 的方法按序组装construct()方法内按顺序调用builder.buildXxx()Client客户端创建 Director 和 ConcreteBuilder将 Builder 注入 Director调用construct()new Director(builder)director.construct()类图识别要点软考类图中建造者模式一定有Director类包含construct()且 Director 与 Builder 是关联关系Director 持有 Builder 引用。如果类图里没有 Director只有 Builder 链式调用那是简化版建造者Fluent Builder不是 GoF 标准建造者。三、场景一电脑组装最经典的建造者模式业务背景电脑由 CPU、主板、内存、硬盘、显卡等多个部件组成。组装流程步骤是固定的先装主板 → 再装 CPU → 再装内存 → 再装硬盘 → 最后装显卡。但高端电脑和普通电脑的每个部件型号不同。Director 负责规定 按什么顺序装Builder 负责 装什么型号。说明这是 GoF 原书中建造者模式的经典例子也是软考下午题最爱考的复杂对象分步组装场景。3.1 代码实现// Product电脑产品 public class Computer { private String cpu; private String motherboard; private String memory; private String hardDisk; private String graphicsCard; public void setCpu(String cpu) { this.cpu cpu; } public void setMotherboard(String motherboard) { this.motherboard motherboard; } public void setMemory(String memory) { this.memory memory; } public void setHardDisk(String hardDisk) { this.hardDisk hardDisk; } public void setGraphicsCard(String graphicsCard) { this.graphicsCard graphicsCard; } Override public String toString() { return Computer [CPU cpu , 主板 motherboard , 内存 memory , 硬盘 hardDisk , 显卡 graphicsCard ]; } } // Builder抽象建造者 public interface ComputerBuilder { void buildMotherboard(); // 步骤1装主板 void buildCpu(); // 步骤2装CPU void buildMemory(); // 步骤3装内存 void buildHardDisk(); // 步骤4装硬盘 void buildGraphicsCard(); // 步骤5装显卡 Computer getResult(); // 返回组装好的电脑 } // ConcreteBuilder高端电脑建造者 public class HighEndComputerBuilder implements ComputerBuilder { private Computer computer new Computer(); Override public void buildMotherboard() { computer.setMotherboard(ROG Z790 主板); } Override public void buildCpu() { computer.setCpu(Intel i9-14900K); } Override public void buildMemory() { computer.setMemory(DDR5 64GB 6000MHz); } Override public void buildHardDisk() { computer.setHardDisk(Samsung 990 Pro 2TB); } Override public void buildGraphicsCard() { computer.setGraphicsCard(RTX 4090); } Override public Computer getResult() { return computer; } } // ConcreteBuilder办公电脑建造者 public class OfficeComputerBuilder implements ComputerBuilder { private Computer computer new Computer(); Override public void buildMotherboard() { computer.setMotherboard(华硕 B760 主板); } Override public void buildCpu() { computer.setCpu(Intel i5-13400); } Override public void buildMemory() { computer.setMemory(DDR4 16GB 3200MHz); } Override public void buildHardDisk() { computer.setHardDisk(WD Blue 512GB SSD); } Override public void buildGraphicsCard() { computer.setGraphicsCard(核显); } Override public Computer getResult() { return computer; } } // Director指挥者定义组装步骤顺序 public class ComputerDirector { private ComputerBuilder builder; public ComputerDirector(ComputerBuilder builder) { this.builder builder; } public void construct() { builder.buildMotherboard(); // 步骤1 builder.buildCpu(); // 步骤2 builder.buildMemory(); // 步骤3 builder.buildHardDisk(); // 步骤4 builder.buildGraphicsCard(); // 步骤5 } } // Client客户端 public class Client { public static void main(String[] args) { ComputerBuilder highEndBuilder new HighEndComputerBuilder(); ComputerDirector director new ComputerDirector(highEndBuilder); director.construct(); Computer highEndPC highEndBuilder.getResult(); System.out.println(highEndPC); ComputerBuilder officeBuilder new OfficeComputerBuilder(); director new ComputerDirector(officeBuilder); director.construct(); Computer officePC officeBuilder.getResult(); System.out.println(officePC); } }关键体会Director 的construct()方法定义了固定的组装步骤算法无论造高端电脑还是办公电脑步骤顺序完全一样。变化的是 Builder 里每个步骤的具体实现用什么型号的部件。这就是 同样的构建过程不同的表示。四、场景二报表生成器Director 控制可选步骤业务背景系统需要生成 PDF 报表和 Excel 报表。报表的生成步骤固定创建页眉 → 创建数据表格 → 创建页脚。但 PDF 和 Excel 的每个步骤实现不同。Director 还可以根据参数控制是否生成页眉/页脚比如导出纯数据时不生成页眉页脚。说明这是软考下午题中常见的带条件控制的建造者场景Director 的construct()方法里可能有if判断。4.1 代码实现// Product报表 public class Report { private String header ; private String body ; private String footer ; public void setHeader(String header) { this.header header; } public void setBody(String body) { this.body body; } public void setFooter(String footer) { this.footer footer; } public void show() { System.out.println( 报表 ); if (!header.isEmpty()) System.out.println(header); System.out.println(body); if (!footer.isEmpty()) System.out.println(footer); System.out.println(); } } // Builder抽象建造者 public interface ReportBuilder { void buildHeader(String title); void buildBody(String[][] data); void buildFooter(String date); Report getResult(); } // ConcreteBuilderPDF 报表建造者 public class PdfReportBuilder implements ReportBuilder { private Report report new Report(); Override public void buildHeader(String title) { report.setHeader([PDF 页眉] 标题: title | 格式: A4 纵向); } Override public void buildBody(String[][] data) { StringBuilder sb new StringBuilder([PDF 表格]\n); for (String[] row : data) { sb.append( ).append(String.join( | , row)).append(\n); } report.setBody(sb.toString()); } Override public void buildFooter(String date) { report.setFooter([PDF 页脚] 生成日期: date | 第 1 页); } Override public Report getResult() { return report; } } // ConcreteBuilderExcel 报表建造者 public class ExcelReportBuilder implements ReportBuilder { private Report report new Report(); Override public void buildHeader(String title) { report.setHeader([Excel 表头] Sheet1: title); } Override public void buildBody(String[][] data) { StringBuilder sb new StringBuilder([Excel 数据区]\n); for (String[] row : data) { sb.append( ).append(String.join(\t, row)).append(\n); } report.setBody(sb.toString()); } Override public void buildFooter(String date) { report.setFooter([Excel 备注] 导出时间: date); } Override public Report getResult() { return report; } } // Director指挥者带可选步骤控制 public class ReportDirector { private ReportBuilder builder; public ReportDirector(ReportBuilder builder) { this.builder builder; } public void constructFull(String title, String[][] data, String date) { builder.buildHeader(title); builder.buildBody(data); builder.buildFooter(date); } public void constructDataOnly(String[][] data) { builder.buildBody(data); } } // Client客户端 public class Client { public static void main(String[] args) { String[][] salesData { {产品, 销量, 金额}, {笔记本, 120, 60万}, {手机, 300, 90万} }; ReportBuilder pdfBuilder new PdfReportBuilder(); ReportDirector director new ReportDirector(pdfBuilder); director.constructFull(2024年Q2销售报表, salesData, 2024-07-01); pdfBuilder.getResult().show(); ReportBuilder excelBuilder new ExcelReportBuilder(); director new ReportDirector(excelBuilder); director.constructDataOnly(salesData); excelBuilder.getResult().show(); } }五、场景三链式调用建造者Fluent Builder简化版业务背景Java 中StringBuilder、StringBuffer以及 Lombok 的Builder注解都是建造者模式的简化实践。这种写法不需要 Director客户端直接链式调用 Builder 的方法最后调用build()获取产品。说明Fluent Builder 没有 DirectorBuilder 自己管理构建步骤。软考中如果类图里没有 Director但有一连串buildXxx()方法链式调用通常也归为建造者模式。5.1 代码实现// Product用户对象 public class User { private final String id; private final String name; private final int age; private final String email; private final String phone; private User(Builder builder) { this.id builder.id; this.name builder.name; this.age builder.age; this.email builder.email; this.phone builder.phone; } Override public String toString() { return User{id id , name name , age age , email email , phone phone }; } // 静态内部类 Builder public static class Builder { private String id; private String name; private int age; private String email; private String phone; public Builder id(String id) { this.id id; return this; } public Builder name(String name) { this.name name; return this; } public Builder age(int age) { this.age age; return this; } public Builder email(String email) { this.email email; return this; } public Builder phone(String phone) { this.phone phone; return this; } public User build() { return new User(this); } } } // Client客户端 public class Client { public static void main(String[] args) { User user new User.Builder() .id(U1001) .name(张三) .age(28) .email(zhangsanexample.com) .phone(13800138000) .build(); System.out.println(user); User simpleUser new User.Builder() .id(U1002) .name(李四) .build(); System.out.println(simpleUser); } }关键体会Fluent Builder 把 Director 的职责隐式地交给了客户端客户端自己决定调用哪些buildXxx()方法但核心的 分步构建 最终组装 思想没变。软考中如果考这种变体通常会在题干中明确说明是 建造者模式的简化形式。六、三种场景对比与演进思路维度场景一电脑组装场景二报表生成器场景三Fluent Builder是否有 Director✅ 有标准 GoF✅ 有带条件控制❌ 无客户端自行控制构建步骤固定 5 步不可变固定 3 步但 Director 可跳过某些步骤客户端自由组合Product 数量1 个Computer1 个Report1 个UserConcreteBuilder 数量2 个高端/办公2 个PDF/Excel1 个内部 Builder 类典型应用复杂对象组装多格式导出多参数对象构造软考考法下午大题补全 Director/Builder下午大题带 if 的 construct上午选择题识别链式调用七、软考高频考点与易混淆辨析7.1 高频考点考点内容模式分类创建型模式GoF 23 正式成员核心四角色Product、Builder、ConcreteBuilder、DirectorDirector 职责定义构建步骤的顺序算法不直接创建产品Builder 职责定义每个步骤的接口由 ConcreteBuilder 实现具体细节与工厂方法区别工厂方法创建单个产品关注创建谁建造者分步组装复杂产品关注怎么一步步组装与抽象工厂区别抽象工厂创建一族相关的产品多个产品建造者创建一个复杂的产品多个部件组装成一个代码填空重点Director 的construct()按顺序调用builder.buildXxx()ConcreteBuilder 的getResult()返回最终产品7.2 易混淆辨析建造者 vs 工厂方法 vs 抽象工厂对比项工厂方法抽象工厂建造者关注点创建哪种产品创建哪一族产品产品怎么一步步组装产品复杂度简单对象多个相关对象一个复杂对象多部件是否有步骤无步骤一步到位无步骤一步到位有固定步骤分步构建Director 角色❌ 无❌ 无✅ 有定义步骤顺序Client 参与选择工厂子类选择工厂子类选择 Builder 调用 Director软考难度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐记忆口诀工厂方法问造什么抽象工厂问造哪套建造者问怎么一步步造。八、真题风格模拟与代码填空模拟题 1上午选择题以下关于建造者模式的叙述中正确的是。A. 建造者模式属于结构型模式主要用于将复杂对象的构建算法与具体实现分离B. 在建造者模式中Director 负责定义创建产品各部件的接口Builder 负责定义组装步骤C. 建造者模式与工厂方法模式的核心区别在于建造者模式关注复杂对象的逐步组装过程工厂方法模式关注创建哪种具体产品D. 建造者模式要求产品必须有多个不同的 ConcreteBuilder否则无法使用答案C解析A 错误建造者属于创建型模式不是结构型。B 错误职责说反了。Director 定义组装步骤算法Builder 定义各部件接口。C 正确这是两者的核心区别。工厂方法解决创建谁建造者解决怎么一步步组装。D 错误建造者可以只有一个 ConcreteBuilder比如场景三的 Fluent BuilderDirector 也可以省略。模拟题 2下午代码填空 — 标准建造者某系统使用建造者模式构建汽车对象Car包含引擎engine、轮胎wheel、车身body三个部件。请补全1~5。javaclass Car { private String engine; private String wheel; private String body; public void setEngine(String engine) { this.engine engine; } public void setWheel(String wheel) { this.wheel wheel; } public void setBody(String body) { this.body body; } public String toString() { return Car[ engine , wheel , body ]; } } interface CarBuilder { void buildEngine(); void buildWheel(); void buildBody(); (1)______ getResult(); } class SportsCarBuilder implements CarBuilder { private Car car new Car(); public void buildEngine() { car.setEngine(V8 双涡轮); } public void buildWheel() { car.setWheel(20寸运动轮毂); } public void buildBody() { car.setBody(碳纤维车身); } public (2)______ getResult() { return (3)______; } } class CarDirector { private (4)______ builder; public CarDirector((5)______ builder) { this.builder builder; } public void construct() { builder.buildEngine(); builder.buildWheel(); builder.buildBody(); } } public class Client { public static void main(String[] args) { CarBuilder builder new SportsCarBuilder(); CarDirector director new CarDirector(builder); director.construct(); Car car builder.getResult(); System.out.println(car); } }答案(1)Car(2)Car(3)car(4)CarBuilder(5)CarBuilder阅卷要点(1)(2) 必须是Car返回产品类型不能写SportsCarBuilder或其他具体类(4)(5) 必须是CarBuilder抽象建造者类型体现面向接口编程。这是建造者模式代码填空的核心判分点。模拟题 3下午代码填空 — Director 带条件控制某系统使用建造者模式生成邮件Mail包含标题title、正文content、附件attachment。Director 根据参数决定是否生成附件。请补全1~3。javaclass Mail { private String title; private String content; private String attachment; public void setTitle(String t) { this.title t; } public void setContent(String c) { this.content c; } public void setAttachment(String a) { this.attachment a; } public void show() { System.out.println(title \n content (attachment ! null ? \n附件: attachment : )); } } interface MailBuilder { void buildTitle(String t); void buildContent(String c); void buildAttachment(String a); Mail getResult(); } class HtmlMailBuilder implements MailBuilder { private Mail mail new Mail(); public void buildTitle(String t) { mail.setTitle(h1 t /h1); } public void buildContent(String c) { mail.setContent(p c /p); } public void buildAttachment(String a) { mail.setAttachment(a href a 下载/a); } public Mail getResult() { return mail; } } class MailDirector { private MailBuilder builder; public MailDirector(MailBuilder builder) { this.builder builder; } public void construct(String title, String content, String attachment, boolean hasAttachment) { builder.buildTitle(title); builder.buildContent(content); if ((1)______) { (2)______; } } } public class Client { public static void main(String[] args) { MailBuilder builder new HtmlMailBuilder(); MailDirector director new MailDirector(builder); director.construct(通知, 会议改期, agenda.pdf, (3)______); builder.getResult().show(); } }答案(1)hasAttachment(2)builder.buildAttachment(attachment)(3)true阅卷要点(2) 必须写成builder.buildAttachment(attachment)不能省略builder.前缀因为 Director 持有的是 Builder 接口引用。(3) 填true表示生成附件若填false则不生成附件两者都算对但需与业务逻辑一致。九、常见陷阱与注意事项陷阱 1误认为建造者是结构型模式建造者属于创建型模式因为它控制对象的创建/组装过程。软考上午题如果问以下属于结构型模式的是选项里出现建造者不能选。陷阱 2Director 和 Builder 的职责混淆这是软考最高频的混淆点Director定义步骤顺序construct()里按顺序调用buildXxx()Builder定义每个步骤的接口buildXxx()方法签名ConcreteBuilder实现每个步骤的具体细节如果代码填空里把 Director 的construct()方法写成了创建具体部件如new Engine()那就是把 Builder 的活抢了判卷时会扣分。陷阱 3忘记getResult()的返回类型是 Product软考代码填空里Builder 接口和 ConcreteBuilder 中的getResult()方法返回值必须是Product 类型如Car不能是具体 Builder 类型。这是面向接口编程原则的体现。陷阱 4与抽象工厂混淆抽象工厂创建多个产品如同时创建按钮和文本框建造者创建一个产品的多个部件如一个电脑的主板CPU内存。软考下午题如果类图里只有一个 Product 类但有很多buildPartXxx()方法是建造者如果有多个 Product 接口Button、TextField是抽象工厂。陷阱 5Fluent Builder 与标准建造者的区别Fluent Builder场景三没有 Director客户端直接链式调用。软考中如果类图里没有 Director 类但题干明确说建造者模式通常指的是这种简化版。但如果下午大题要求补全 Director那一定是标准 GoF 建造者有 Director Builder ConcreteBuilder Product 四角色。陷阱 6Director 注入 Builder 的方式Director 可以通过构造器注入如new Director(builder)或Setter 注入如director.setBuilder(builder)。软考代码填空里两种写法都算对但要注意如果 Director 类里声明的是private Builder builder注入时必须是Builder类型抽象不能是ConcreteBuilder类型具体。十、总结要点内容定义将复杂对象的构建与它的表示分离同样的构建过程可以创建不同的表示分类创建型模式GoF 23 正式成员核心四角色Product产品、Builder抽象建造者、ConcreteBuilder具体建造者、Director指挥者Director 职责定义构建步骤顺序算法按序调用builder.buildXxx()Builder 职责声明各部件的构建接口和getResult()方法ConcreteBuilder 职责实现各部件的具体构建返回最终组装好的产品与工厂方法区别工厂方法创建单个产品关注创建谁建造者分步组装复杂产品关注怎么一步步组装与抽象工厂区别抽象工厂创建一族相关产品多个产品建造者创建一个复杂产品的多个部件一个产品软考重点下午代码填空补全 Director 的construct()步骤顺序Builder 的getResult()返回类型四个角色的识别答题技巧看到分步骤组装复杂对象、Director 定义步骤、Builder 定义实现 → 建造者看到类图里有construct()方法 → 确认建造者系列预告下一篇将讲原型模式—— 当创建新对象的成本较高时如何通过克隆现有对象来快速生成新实例深拷贝与浅拷贝的陷阱咱们逐一拆解。咱们下回见。