【04】【创建型】【聊一聊,建造者模式】

1 概述

1.1 简介

建造者模式(Builder Pattern)是一种创建型设计模式,其核心思想是将复杂对象的构建过程与对象的表示分离,使得同样的构建过程可以创建不同的表示。

复杂对象通常具有多个组成部分(属性或子对象),且构建过程涉及多步逻辑(如初始化、属性赋值、校验、组装等)。建造者模式通过拆分构建逻辑,由专门的"建造者"类负责分步构建对象,再由"指挥者"类(可选)协调建造流程,最终生成完整对象。

适用场景:

  • 对象具有复杂的内部结构(包含多个成员变量、嵌套对象);
  • 对象构建过程需要多步逻辑,且步骤顺序可能灵活调整;
  • 需避免构造方法参数过多导致的" telescoping constructor "问题(构造器臃肿、可读性差);
  • 需支持同一构建过程生成不同配置的对象实例。

1.2 主要角色

建造者模式包含5个核心角色,职责分工明确:

  1. 产品(Product):被构建的复杂对象,包含多个组成部分(属性或子对象),提供基本的属性设置和业务方法。
  2. 抽象建造者(Abstract Builder) :定义构建产品的抽象接口,包含构建产品各组成部分的方法(如 buildPartA()buildPartB())和返回产品的方法(如 getResult())。
  3. 具体建造者(Concrete Builder):实现抽象建造者接口,负责具体的构建逻辑(如给产品属性赋值、校验参数、组装子对象),不同的具体建造者可生成不同配置的产品。
  4. 指挥者(Director):负责协调建造流程,定义构建产品的步骤顺序(如先构建PartA,再构建PartB),调用具体建造者的方法完成对象构建(可选角色,简单场景下可省略,由客户端直接调用建造者)。
  5. 客户端(Client):创建具体建造者和指挥者(若有),通过指挥者或直接调用建造者完成对象构建,最终获取产品实例。

1.3 优点

  1. 解耦构建与表示:构建逻辑封装在建造者类中,产品的表示(属性配置)与构建过程分离,同一构建流程可生成不同产品。
  2. 简化对象创建:避免复杂对象的臃肿构造器,通过分步调用建造方法设置属性,可读性和易用性更高。
  3. 灵活控制构建过程:可通过指挥者调整构建步骤顺序,或通过不同的具体建造者生成不同配置的产品,扩展性强。
  4. 职责单一:产品类负责封装业务逻辑,建造者类负责构建流程,指挥者类负责协调步骤,符合单一职责原则。
  5. 便于参数校验:在建造者的构建方法中可对参数进行校验(如非空、范围校验),确保产品实例的合法性。

1.4 缺点

  1. 增加类复杂度:相比直接创建对象,需额外定义抽象建造者、具体建造者、指挥者等类,增加了系统的类数量和理解成本。
  2. 仅适用于复杂对象:若对象结构简单(仅少数属性),使用建造者模式会显得冗余,不如直接通过构造器或setter方法高效。
  3. 建造者与产品耦合度高:具体建造者需深入了解产品的内部结构(如属性、子对象),若产品结构修改,对应的建造者也需同步修改,维护成本较高。
  4. 灵活性有限:若产品的组成部分(属性)频繁变更,需频繁修改抽象建造者和具体建造者的接口/方法,扩展性受限于初始设计。

2 实现

2.1.1 类图(mermaid语法)

Product - partA: String - partB: String - partC: String +setPartA(a: String) +setPartB(b: String) +setPartC(c: String) +show() AbstractBuilder +abstract buildPartA() +abstract buildPartB() +abstract buildPartC() +abstract getResult() ConcreteBuilder1 - product: Product +ConcreteBuilder1() +buildPartA() +buildPartB() +buildPartC() +getResult() ConcreteBuilder2 - product: Product +ConcreteBuilder2() +buildPartA() +buildPartB() +buildPartC() +getResult() Director - builder: AbstractBuilder +Director(builder: AbstractBuilder) +construct()

2.3.2 示例代码

以下以"电脑组装"为例,实现建造者模式:

  • 产品(Product):电脑(Computer),包含CPU、内存、硬盘、显卡等组成部分;
  • 抽象建造者(AbstractBuilder):电脑建造者接口(ComputerBuilder);
  • 具体建造者:游戏本建造者(GamingLaptopBuilder)、办公本建造者(OfficeLaptopBuilder);
  • 指挥者(Director):电脑组装指挥者(ComputerAssembler),负责协调组装步骤。

1. 产品类(Product)

java 复制代码
// 电脑产品类
public class Computer {
    // 产品组成部分
    private String cpu;
    private String ram;
    private String hardDisk;
    private String graphicsCard;
    private String keyboard;

    // setter方法(建造者通过setter设置属性)
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public void setRam(String ram) {
        this.ram = ram;
    }

    public void setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
    }

    public void setGraphicsCard(String graphicsCard) {
        this.graphicsCard = graphicsCard;
    }

    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }

    // 展示电脑配置
    public void showConfig() {
        System.out.println("电脑配置:");
        System.out.println("CPU:" + cpu);
        System.out.println("内存:" + ram);
        System.out.println("硬盘:" + hardDisk);
        System.out.println("显卡:" + graphicsCard);
        System.out.println("键盘:" + keyboard);
    }
}

2. 抽象建造者(Abstract Builder)

java 复制代码
// 电脑建造者抽象接口
public interface ComputerBuilder {
    // 构建各组成部分
    void buildCpu();
    void buildRam();
    void buildHardDisk();
    void buildGraphicsCard();
    void buildKeyboard();

    // 返回构建完成的电脑产品
    Computer getResult();
}

3. 具体建造者(Concrete Builder)

java 复制代码
// 游戏本建造者(高性能配置)
public class GamingLaptopBuilder implements ComputerBuilder {
    private Computer computer;

    // 初始化产品实例
    public GamingLaptopBuilder() {
        this.computer = new Computer();
    }

    @Override
    public void buildCpu() {
        computer.setCpu("Intel i9-13900HX(高性能处理器)");
    }

    @Override
    public void buildRam() {
        computer.setRam("64GB DDR5 5600MHz(超大内存)");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("2TB NVMe SSD(高速硬盘)");
    }

    @Override
    public void buildGraphicsCard() {
        computer.setGraphicsCard("NVIDIA RTX 4090(顶级独显)");
    }

    @Override
    public void buildKeyboard() {
        computer.setKeyboard("RGB机械键盘(全键无冲)");
    }

    @Override
    public Computer getResult() {
        return this.computer;
    }
}

// 办公本建造者(轻薄节能配置)
public class OfficeLaptopBuilder implements ComputerBuilder {
    private Computer computer;

    public OfficeLaptopBuilder() {
        this.computer = new Computer();
    }

    @Override
    public void buildCpu() {
        computer.setCpu("Intel i5-1340P(低功耗处理器)");
    }

    @Override
    public void buildRam() {
        computer.setRam("16GB DDR5 4800MHz(够用内存)");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("512GB NVMe SSD(日常存储)");
    }

    @Override
    public void buildGraphicsCard() {
        computer.setGraphicsCard("Intel UHD Graphics(集成显卡)");
    }

    @Override
    public void buildKeyboard() {
        computer.setKeyboard("静音巧克力键盘(轻薄便携)");
    }

    @Override
    public Computer getResult() {
        return this.computer;
    }
}

4. 指挥者(Director)

java 复制代码
// 电脑组装指挥者(协调构建步骤)
public class ComputerAssembler {
    private ComputerBuilder builder;

    // 注入具体建造者
    public ComputerAssembler(ComputerBuilder builder) {
        this.builder = builder;
    }

    // 定义构建流程(步骤顺序固定,可灵活调整)
    public void assembleComputer() {
        builder.buildCpu();    // 第一步:装CPU
        builder.buildRam();    // 第二步:装内存
        builder.buildHardDisk();// 第三步:装硬盘
        builder.buildGraphicsCard();// 第四步:装显卡
        builder.buildKeyboard();// 第五步:装键盘
    }
}

5. 客户端调用

java 复制代码
public class Client {
    public static void main(String[] args) {
        // 1. 构建游戏本
        ComputerBuilder gamingBuilder = new GamingLaptopBuilder();
        ComputerAssembler gamingAssembler = new ComputerAssembler(gamingBuilder);
        gamingAssembler.assembleComputer(); // 指挥者协调构建
        Computer gamingLaptop = gamingBuilder.getResult();
        System.out.println("=== 游戏本配置 ===");
        gamingLaptop.showConfig();

        System.out.println("\n" + "=".repeat(50) + "\n");

        // 2. 构建办公本
        ComputerBuilder officeBuilder = new OfficeLaptopBuilder();
        ComputerAssembler officeAssembler = new ComputerAssembler(officeBuilder);
        officeAssembler.assembleComputer();
        Computer officeLaptop = officeBuilder.getResult();
        System.out.println("=== 办公本配置 ===");
        officeLaptop.showConfig();
    }
}

输出结果

复制代码
=== 游戏本配置 ===
电脑配置:
CPU:Intel i9-13900HX(高性能处理器)
内存:64GB DDR5 5600MHz(超大内存)
硬盘:2TB NVMe SSD(高速硬盘)
显卡:NVIDIA RTX 4090(顶级独显)
键盘:RGB机械键盘(全键无冲)

==================================================

=== 办公本配置 ===
电脑配置:
CPU:Intel i5-1340P(低功耗处理器)
内存:16GB DDR5 4800MHz(够用内存)
硬盘:512GB NVMe SSD(日常存储)
显卡:Intel UHD Graphics(集成显卡)
键盘:静音巧克力键盘(轻薄便携)

3 具体应用

3.1 应用场景1:HTTP请求构建(模拟OkHttp的Request.Builder)

背景

HTTP请求是典型的复杂对象,包含请求URL、请求方法(GET/POST)、请求头(Headers)、请求体(Body)、超时时间、拦截器等多个组成部分,且不同请求的配置差异较大(如GET请求无Body,POST请求需设置Content-Type)。

使用建造者模式可简化HTTP请求的构建过程,避免构造器参数臃肿,同时支持灵活配置请求参数。

实现示例

java 复制代码
import java.util.HashMap;
import java.util.Map;

// 1. 产品类:HTTP请求对象
class HttpRequest {
    private String url;
    private String method; // GET/POST/PUT/DELETE
    private Map<String, String> headers;
    private String body;
    private int timeout; // 超时时间(毫秒)

    // 私有构造方法(仅允许Builder创建)
    private HttpRequest(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers;
        this.body = builder.body;
        this.timeout = builder.timeout;
    }

    //  getter方法(用于后续发送请求时获取参数)
    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public Map<String, String> getHeaders() { return headers; }
    public String getBody() { return body; }
    public int getTimeout() { return timeout; }

    // 展示请求信息
    public void showRequest() {
        System.out.println("HTTP请求信息:");
        System.out.println("Method:" + method);
        System.out.println("URL:" + url);
        System.out.println("Headers:" + headers);
        System.out.println("Body:" + body);
        System.out.println("Timeout:" + timeout + "ms");
    }

    // 2. 内部建造者类(省略抽象建造者和指挥者,简化设计)
    public static class Builder {
        private String url; // 必传参数
        private String method = "GET"; // 默认GET方法
        private Map<String, String> headers = new HashMap<>();
        private String body;
        private int timeout = 5000; // 默认超时5秒

        // 构造器:必传参数(URL)
        public Builder(String url) {
            if (url == null || url.isEmpty()) {
                throw new IllegalArgumentException("URL不能为空");
            }
            this.url = url;
        }

        // 可选配置:请求方法
        public Builder method(String method) {
            this.method = method.toUpperCase();
            return this; // 链式调用
        }

        // 可选配置:添加请求头
        public Builder addHeader(String key, String value) {
            this.headers.put(key, value);
            return this;
        }

        // 可选配置:请求体
        public Builder body(String body) {
            this.body = body;
            return this;
        }

        // 可选配置:超时时间
        public Builder timeout(int timeout) {
            if (timeout < 0) {
                throw new IllegalArgumentException("超时时间不能为负数");
            }
            this.timeout = timeout;
            return this;
        }

        // 构建请求对象
        public HttpRequest build() {
            // 校验:POST请求必须有Body(示例校验逻辑)
            if ("POST".equals(method) && (body == null || body.isEmpty())) {
                throw new IllegalStateException("POST请求必须设置请求体");
            }
            return new HttpRequest(this);
        }
    }
}

// 3. 客户端调用(模拟发送HTTP请求)
public class HttpBuilderDemo {
    public static void main(String[] args) {
        // 构建GET请求(无Body,仅配置URL、超时、请求头)
        HttpRequest getRequest = new HttpRequest.Builder("https://api.example.com/user/1")
                .method("GET")
                .addHeader("Accept", "application/json")
                .timeout(3000)
                .build();
        System.out.println("=== GET请求 ===");
        getRequest.showRequest();

        System.out.println("\n" + "=".repeat(50) + "\n");

        // 构建POST请求(需配置Body、Content-Type)
        HttpRequest postRequest = new HttpRequest.Builder("https://api.example.com/user")
                .method("POST")
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", "Bearer token123")
                .body("{\"name\":\"张三\",\"age\":25}")
                .timeout(10000)
                .build();
        System.out.println("=== POST请求 ===");
        postRequest.showRequest();
    }
}

输出结果

复制代码
=== GET请求 ===
HTTP请求信息:
Method:GET
URL:https://api.example.com/user/1
Headers:{Accept=application/json}
Body:null
Timeout:3000ms

==================================================

=== POST请求 ===
HTTP请求信息:
Method:POST
URL:https://api.example.com/user
Headers:{Content-Type=application/json, Authorization=Bearer token123}
Body:{"name":"张三","age":25}
Timeout:10000ms

核心价值

  • 链式调用简化配置:通过 Builder 的链式方法,清晰设置请求参数,可读性远高于多参数构造器;
  • 必传参数校验:通过Builder构造器强制传入必传参数(URL),避免遗漏;
  • 灵活配置:支持按需设置请求方法、请求头、请求体等,默认值合理,减少重复代码;
  • 参数合法性校验:在 build() 方法中校验POST请求必须有Body等规则,确保请求对象合法。

3.2 应用场景2:文档生成器(Word/PDF文档构建)

背景

文档生成(如Word、PDF)是复杂对象构建场景:文档包含标题、正文、图片、表格、页眉页脚、格式设置(字体、颜色、对齐方式)等多个组成部分,且不同类型文档(如报告、简历、合同)的结构和格式差异较大。

使用建造者模式可拆分不同文档的构建逻辑,通过不同的具体建造者生成不同格式的文档,指挥者统一协调文档生成步骤(如先加标题、再加正文、最后加页眉页脚)。

实现示例

java 复制代码
// 1. 产品类:文档对象
interface Document {
    void addTitle(String title);
    void addContent(String content);
    void addImage(String imagePath);
    void addFooter(String footer);
    void showDocument(); // 展示文档内容(模拟生成后的预览)
}

// 具体产品:Word文档
class WordDocument implements Document {
    private StringBuilder content = new StringBuilder();

    @Override
    public void addTitle(String title) {
        content.append("[Word标题]:").append(title).append("\n");
    }

    @Override
    public void addContent(String content) {
        this.content.append("[Word正文]:").append(content).append("\n");
    }

    @Override
    public void addImage(String imagePath) {
        this.content.append("[Word图片]:").append(imagePath).append("(Word格式优化)\n");
    }

    @Override
    public void addFooter(String footer) {
        this.content.append("[Word页脚]:").append(footer).append("\n");
    }

    @Override
    public void showDocument() {
        System.out.println("=== Word文档内容 ===");
        System.out.println(content.toString());
    }
}

// 具体产品:PDF文档
class PdfDocument implements Document {
    private StringBuilder content = new StringBuilder();

    @Override
    public void addTitle(String title) {
        content.append("[PDF标题]:").append(title).append("(PDF加粗居中)\n");
    }

    @Override
    public void addContent(String content) {
        this.content.append("[PDF正文]:").append(content).append("(PDF段落格式)\n");
    }

    @Override
    public void addImage(String imagePath) {
        this.content.append("[PDF图片]:").append(imagePath).append("(PDF压缩优化)\n");
    }

    @Override
    public void addFooter(String footer) {
        this.content.append("[PDF页脚]:").append(footer).append("(PDF右对齐)\n");
    }

    @Override
    public void showDocument() {
        System.out.println("=== PDF文档内容 ===");
        System.out.println(content.toString());
    }
}

// 2. 抽象建造者:文档建造者
interface DocumentBuilder {
    void buildTitle(String title);
    void buildContent(String content);
    void buildImage(String imagePath);
    void buildFooter(String footer);
    Document getDocument();
}

// 3. 具体建造者:Word文档建造者
class WordDocumentBuilder implements DocumentBuilder {
    private WordDocument document = new WordDocument();

    @Override
    public void buildTitle(String title) {
        document.addTitle(title);
    }

    @Override
    public void buildContent(String content) {
        document.addContent(content);
    }

    @Override
    public void buildImage(String imagePath) {
        document.addImage(imagePath);
    }

    @Override
    public void buildFooter(String footer) {
        document.addFooter(footer);
    }

    @Override
    public Document getDocument() {
        return document;
    }
}

// 具体建造者:PDF文档建造者
class PdfDocumentBuilder implements DocumentBuilder {
    private PdfDocument document = new PdfDocument();

    @Override
    public void buildTitle(String title) {
        document.addTitle(title);
    }

    @Override
    public void buildContent(String content) {
        document.addContent(content);
    }

    @Override
    public void buildImage(String imagePath) {
        document.addImage(imagePath);
    }

    @Override
    public void buildFooter(String footer) {
        document.addFooter(footer);
    }

    @Override
    public Document getDocument() {
        return document;
    }
}

// 4. 指挥者:文档生成指挥者
class DocumentDirector {
    private DocumentBuilder builder;

    public DocumentDirector(DocumentBuilder builder) {
        this.builder = builder;
    }

    // 定义文档生成流程(固定步骤:标题→正文→图片→页脚)
    public void generateDocument(String title, String content, String imagePath, String footer) {
        builder.buildTitle(title);
        builder.buildContent(content);
        if (imagePath != null && !imagePath.isEmpty()) {
            builder.buildImage(imagePath);
        }
        builder.buildFooter(footer);
    }
}

// 5. 客户端调用
public class DocumentBuilderDemo {
    public static void main(String[] args) {
        // 生成Word文档
        DocumentBuilder wordBuilder = new WordDocumentBuilder();
        DocumentDirector wordDirector = new DocumentDirector(wordBuilder);
        wordDirector.generateDocument(
                "项目需求文档",
                "本文档描述了XX项目的功能需求和技术规格...",
                "D:/project/cover.png",
                "版本:V1.0 | 日期:2025-12-01"
        );
        Document wordDoc = wordBuilder.getDocument();
        wordDoc.showDocument();

        System.out.println("\n" + "=".repeat(80) + "\n");

        // 生成PDF文档
        DocumentBuilder pdfBuilder = new PdfDocumentBuilder();
        DocumentDirector pdfDirector = new DocumentDirector(pdfBuilder);
        pdfDirector.generateDocument(
                "个人简历",
                "姓名:张三 | 职位:Java开发工程师 | 工作年限:3年...",
                "D:/resume/photo.jpg",
                "联系电话:138XXXX1234 | 邮箱:zhangsan@example.com"
        );
        Document pdfDoc = pdfBuilder.getDocument();
        pdfDoc.showDocument();
    }
}

输出结果

复制代码
=== Word文档内容 ===
[Word标题]:项目需求文档
[Word正文]:本文档描述了XX项目的功能需求和技术规格...
[Word图片]:D:/project/cover.png(Word格式优化)
[Word页脚]:版本:V1.0 | 日期:2025-12-01

================================================================================

=== PDF文档内容 ===
[PDF标题]:个人简历(PDF加粗居中)
[PDF正文]:姓名:张三 | 职位:Java开发工程师 | 工作年限:3年...(PDF段落格式)
[PDF图片]:D:/resume/photo.jpg(PDF压缩优化)
[PDF页脚]:联系电话:138XXXX1234 | 邮箱:zhangsan@example.com(PDF右对齐)

核心价值

  • 统一构建流程:指挥者 DocumentDirector 定义了"标题→正文→图片→页脚"的固定流程,确保所有文档生成遵循统一规范;
  • 不同格式隔离:WordDocumentBuilderPdfDocumentBuilder 分别实现不同文档的格式处理(如Word图片优化、PDF加粗居中),逻辑隔离,互不影响;
  • 扩展性强:若后续需要支持Excel文档,只需新增 ExcelDocument(产品)和 ExcelDocumentBuilder(具体建造者),无需修改现有代码,符合开闭原则;
  • 简化客户端使用:客户端无需关注文档构建的细节步骤,只需传入内容参数,通过指挥者即可生成目标格式的文档。
相关推荐
blackorbird2 小时前
苹果修复了两个在定向攻击中被利用的Webkit漏洞,其中一个与谷歌ANGLE漏洞同源
前端·webkit
风无雨2 小时前
在 React 中实现数学公式显示:使用 KaTeX 和 react-katex
前端·react.js·前端框架
原来是好奇心2 小时前
深入Spring Boot源码(四):Starter机制与依赖管理深度解析
java·源码·springboot·starter
阿杆2 小时前
如何在 Spring Boot 中接入 Amazon ElastiCache
java·数据库·redis
cheems95272 小时前
锁策略的介绍
java·开发语言
武子康2 小时前
Java-199 JMS Queue/Topic 集群下如何避免重复消费:ActiveMQ 虚拟主题与交付语义梳理
java·分布式·消息队列·rabbitmq·activemq·mq·java-activemq
LSL666_2 小时前
12 MyBatis的连接池
java·服务器·mybatis
Arva .2 小时前
说说线程的生命周期和状态
java·开发语言
二两锅巴2 小时前
📺 无需Electron!前端实现多显示器浏览器窗口精准控制与通信
前端