设计模式:构建者模式

目录

一、建造者模式的核心本质

二、建造者模式的完整结构(经典版)

[1. 核心角色分工](#1. 核心角色分工)

[2. 经典版代码实现(Java)](#2. 经典版代码实现(Java))

[步骤 1:定义产品类(Computer)](#步骤 1:定义产品类(Computer))

[步骤 2:定义抽象建造者(ComputerBuilder)](#步骤 2:定义抽象建造者(ComputerBuilder))

[步骤 3:定义具体建造者(游戏本 / 办公本)](#步骤 3:定义具体建造者(游戏本 / 办公本))

[步骤 4:定义指挥者(ComputerDirector)](#步骤 4:定义指挥者(ComputerDirector))

[步骤 5:客户端使用](#步骤 5:客户端使用)

输出结果

三、建造者模式的简化版(实战常用)

简化版代码(电脑场景)

客户端使用(链式调用)

四、建造者模式的关键细节

[1. 什么时候必须用建造者模式?](#1. 什么时候必须用建造者模式?)

[2. 建造者模式的变种](#2. 建造者模式的变种)

[3. Lombok 简化建造者模式](#3. Lombok 简化建造者模式)

[4. 建造者模式的坑](#4. 建造者模式的坑)

五、建造者模式与其他创建型模式的区别

[1. 建造者模式 vs 工厂模式](#1. 建造者模式 vs 工厂模式)

[2. 建造者模式 vs 原型模式](#2. 建造者模式 vs 原型模式)

六、实战场景举例(无人售货柜项目)

七、总结

核心要点

关键提醒


一、建造者模式的核心本质

建造者模式是创建型设计模式的核心成员,它解决的核心痛点是:

  • 当一个对象属性多、层级深、可选配置多(比如有 10 个以上属性,部分必填、部分可选),如果用普通构造器会出现 "参数爆炸"(Constructor Telescoping),可读性和可维护性极差;
  • 希望把对象的 "构建步骤" 和 "具体配置" 分开,让构建流程标准化,配置个性化;
  • 确保对象构建过程中状态一致(避免创建出属性不完整的 "半成品" 对象)。

一句话总结:建造者模式 = 标准化构建流程 + 个性化配置 + 保证对象完整性

二、建造者模式的完整结构(经典版)

经典建造者模式包含 4 个核心角色,缺一不可,我们用 "组装电脑" 这个更直观的场景来拆解:

1. 核心角色分工

角色名称 核心职责 电脑场景示例
产品(Product) 最终要构建的复杂对象,由多个部件组成 Computer(包含 CPU、内存、硬盘、显卡、操作系统等部件)
抽象建造者(Builder) 定义构建产品的所有步骤(抽象方法),包含 "构建部件" 和 "获取产品" 两类方法 ComputerBuilder(抽象方法:buildCPU ()、buildMemory ()、buildHardDisk ()、buildGraphics ()、getComputer ())
具体建造者(ConcreteBuilder) 实现抽象建造者的方法,完成具体部件的构建(比如 "游戏本建造者" 用高性能 CPU,"办公本建造者" 用低功耗 CPU) GameComputerBuilder(游戏本)、OfficeComputerBuilder(办公本)
指挥者(Director) 定义构建的顺序和流程(比如先装 CPU→内存→硬盘→显卡→装系统),调用具体建造者的方法完成构建,隔离用户与构建细节 ComputerDirector(方法:constructComputer (ComputerBuilder builder))

2. 经典版代码实现(Java)

步骤 1:定义产品类(Computer)

java

运行

复制代码
// 复杂产品:电脑
public class Computer {
    // 核心部件(部分必填,部分可选)
    private String cpu;      // 必填
    private String memory;   // 必填
    private String hardDisk; // 必填
    private String graphics; // 可选(办公本可无独立显卡)
    private String os;       // 必填

    // 空构造器(由建造者填充属性)
    public Computer() {}

    // setter方法(建造者调用)
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

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

    public void setGraphics(String graphics) {
        this.graphics = graphics;
    }

    public void setOs(String os) {
        this.os = os;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", memory='" + memory + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                ", graphics='" + graphics + '\'' +
                ", os='" + os + '\'' +
                '}';
    }
}
步骤 2:定义抽象建造者(ComputerBuilder)

java

运行

复制代码
// 抽象建造者:定义构建电脑的所有步骤
public abstract class ComputerBuilder {
    // 持有产品对象(所有具体建造者操作同一个产品实例)
    protected Computer computer = new Computer();

    // 构建核心部件(抽象方法,由具体建造者实现)
    public abstract void buildCPU();
    public abstract void buildMemory();
    public abstract void buildHardDisk();
    public abstract void buildGraphics();
    public abstract void buildOS();

    // 获取构建完成的产品(通用方法,无需子类重写)
    public Computer getComputer() {
        return this.computer;
    }
}
步骤 3:定义具体建造者(游戏本 / 办公本)

java

运行

复制代码
// 具体建造者1:游戏本建造者(高性能配置)
public class GameComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCPU() {
        computer.setCpu("Intel i9-14900K(高性能游戏CPU)");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("32GB DDR5 6000MHz");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("2TB NVMe PCIe4.0 SSD");
    }

    @Override
    public void buildGraphics() {
        computer.setGraphics("NVIDIA RTX 4090 24GB");
    }

    @Override
    public void buildOS() {
        computer.setOs("Windows 11 专业版");
    }
}

// 具体建造者2:办公本建造者(低功耗、高续航)
public class OfficeComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCPU() {
        computer.setCpu("Intel i5-1340P(低功耗办公CPU)");
    }

    @Override
    public void buildMemory() {
        computer.setMemory("16GB DDR4 3200MHz");
    }

    @Override
    public void buildHardDisk() {
        computer.setHardDisk("1TB SATA3 SSD");
    }

    @Override
    public void buildGraphics() {
        computer.setGraphics("集成显卡(UHD Graphics)");
    }

    @Override
    public void buildOS() {
        computer.setOs("Windows 11 家庭版");
    }
}
步骤 4:定义指挥者(ComputerDirector)

java

运行

复制代码
// 指挥者:定义构建流程,隔离用户与构建细节
public class ComputerDirector {
    // 构建电脑的核心方法(参数为抽象建造者,符合开闭原则)
    public Computer constructComputer(ComputerBuilder builder) {
        // 固定构建顺序:CPU → 内存 → 硬盘 → 显卡 → 系统
        builder.buildCPU();
        builder.buildMemory();
        builder.buildHardDisk();
        builder.buildGraphics();
        builder.buildOS();
        // 返回构建完成的产品
        return builder.getComputer();
    }
}
步骤 5:客户端使用

java

运行

复制代码
public class Client {
    public static void main(String[] args) {
        // 1. 创建指挥者(统一管理构建流程)
        ComputerDirector director = new ComputerDirector();

        // 2. 构建游戏本
        ComputerBuilder gameBuilder = new GameComputerBuilder();
        Computer gameComputer = director.constructComputer(gameBuilder);
        System.out.println("游戏本配置:" + gameComputer);

        // 3. 构建办公本
        ComputerBuilder officeBuilder = new OfficeComputerBuilder();
        Computer officeComputer = director.constructComputer(officeBuilder);
        System.out.println("办公本配置:" + officeComputer);
    }
}
输出结果

plaintext

复制代码
游戏本配置:Computer{cpu='Intel i9-14900K(高性能游戏CPU)', memory='32GB DDR5 6000MHz', hardDisk='2TB NVMe PCIe4.0 SSD', graphics='NVIDIA RTX 4090 24GB', os='Windows 11 专业版'}
办公本配置:Computer{cpu='Intel i5-1340P(低功耗办公CPU)', memory='16GB DDR4 3200MHz', hardDisk='1TB SATA3 SSD', graphics='集成显卡(UHD Graphics)', os='Windows 11 家庭版'}

三、建造者模式的简化版(实战常用)

经典版需要创建 4 个类,结构偏复杂,日常开发中更常用 **"静态内部类 + 链式调用"** 的简化版(也叫 "流式建造者"),无需单独的 Director 和抽象 Builder,核心特点:

  1. 产品类中嵌套静态内部 Builder 类;
  2. Builder 类提供链式 setter 方法(返回 this);
  3. Builder 的 build () 方法完成参数校验并创建产品实例;
  4. 产品类构造器私有化,确保只能通过 Builder 创建。

简化版代码(电脑场景)

java

运行

复制代码
public class Computer {
    // 核心属性(final保证不可变,线程安全)
    private final String cpu;
    private final String memory;
    private final String hardDisk;
    private final String graphics;
    private final String os;

    // 私有化构造器,仅内部Builder调用
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.hardDisk = builder.hardDisk;
        this.graphics = builder.graphics;
        this.os = builder.os;
    }

    // 静态内部建造者类
    public static class Builder {
        // 必填属性(无默认值)
        private final String cpu;
        private final String memory;
        private final String os;
        // 可选属性(有默认值)
        private String hardDisk = "512GB SATA3 SSD";
        private String graphics = "集成显卡";

        // Builder构造器:强制传入必填属性
        public Builder(String cpu, String memory, String os) {
            this.cpu = cpu;
            this.memory = memory;
            this.os = os;
        }

        // 链式调用:设置可选属性
        public Builder hardDisk(String hardDisk) {
            this.hardDisk = hardDisk;
            return this;
        }

        public Builder graphics(String graphics) {
            this.graphics = graphics;
            return this;
        }

        // 构建产品:包含参数校验
        public Computer build() {
            // 校验必填属性(非空、合法)
            if (cpu == null || cpu.isEmpty()) {
                throw new IllegalArgumentException("CPU不能为空");
            }
            return new Computer(this);
        }
    }

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", memory='" + memory + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                ", graphics='" + graphics + '\'' +
                ", os='" + os + '\'' +
                '}';
    }
}

客户端使用(链式调用)

java

运行

复制代码
public class Client {
    public static void main(String[] args) {
        // 构建游戏本(自定义可选属性)
        Computer gameComputer = new Computer.Builder(
                "Intel i9-14900K", 
                "32GB DDR5", 
                "Windows 11 专业版"
        )
        .hardDisk("2TB NVMe SSD")
        .graphics("RTX 4090")
        .build();

        // 构建办公本(使用可选属性默认值)
        Computer officeComputer = new Computer.Builder(
                "Intel i5-1340P", 
                "16GB DDR4", 
                "Windows 11 家庭版"
        ).build();

        System.out.println("游戏本:" + gameComputer);
        System.out.println("办公本:" + officeComputer);
    }
}

四、建造者模式的关键细节

1. 什么时候必须用建造者模式?

  • 对象有多个必填属性 + 多个可选属性,且属性之间有依赖关系(比如 "选了独立显卡就必须选高性能电源");
  • 需要控制对象的构建顺序(比如必须先初始化基础配置,再配置扩展功能);
  • 希望避免创建不完整的对象(比如禁止只设置 CPU 就使用电脑);
  • 希望代码可读性高(链式调用比一堆 setter 更直观)。

2. 建造者模式的变种

变种类型 适用场景 示例
经典版(Director + Builder) 构建流程固定、产品类型多(比如游戏本 / 办公本 / 服务器) 框架级代码(如 MyBatis 的 SqlSessionFactoryBuilder)
简化版(静态内部 Builder) 单个产品、配置灵活、无需统一流程 业务层复杂对象(如订单、用户、设备)
流式建造者(Fluent Builder) 追求代码简洁、链式调用 Lombok 的 @Builder 注解

3. Lombok 简化建造者模式

日常开发中可以用 Lombok 的@Builder注解自动生成建造者代码,无需手写:

java

运行

复制代码
import lombok.Builder;
import lombok.ToString;

@Builder // 自动生成静态内部Builder类 + 链式方法 + build()方法
@ToString
public class Computer {
    private String cpu;
    private String memory;
    private String hardDisk;
    private String graphics;
    private String os;
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        Computer computer = Computer.builder()
                .cpu("Intel i9")
                .memory("32GB")
                .graphics("RTX 4090")
                .build();
        System.out.println(computer);
    }
}

4. 建造者模式的坑

  • 不要滥用:简单对象(比如只有 2-3 个属性)用建造者模式会增加代码冗余,直接用构造器 /setter 即可;
  • 注意线程安全:如果 Builder 被多线程调用,需要加锁(但 Builder 通常是局部变量,无需考虑);
  • 参数校验:必须在build()方法中校验必填属性,否则会创建 "半成品" 对象;
  • 避免过度设计:经典版的 Director 和抽象 Builder 只在流程固定时使用,业务层优先用简化版。

五、建造者模式与其他创建型模式的区别

1. 建造者模式 vs 工厂模式

维度 建造者模式 工厂模式(简单工厂 / 工厂方法)
核心目标 关注如何一步步构建复杂对象 关注创建什么对象(不关心构建过程)
产品特点 同一产品的不同配置(比如游戏本和办公本都是电脑,只是配置不同) 不同产品的不同类型(比如工厂生产电脑、手机、平板)
调用方式 链式调用多个方法,最后 build () 调用一个方法直接获取对象
适用场景 复杂对象、配置灵活 简单对象、类型固定

2. 建造者模式 vs 原型模式

  • 建造者模式:从头开始构建对象(一步一步组装部件);
  • 原型模式:复制已有对象(基于原型克隆,无需重新构建)。

六、实战场景举例(无人售货柜项目)

以无人售货柜的 "订单对象" 为例,订单包含:

  • 必填属性:订单号、设备 ID、用户 ID、商品列表、支付金额;
  • 可选属性:优惠券 ID、配送地址、备注、支付方式;
  • 依赖关系:"选择配送模式" 必须填写配送地址。

用建造者模式实现:

java

运行

复制代码
public class VendingOrder {
    // 必填属性
    private final String orderId;
    private final String machineId;
    private final String userId;
    private final List<String> productIds;
    private final BigDecimal payAmount;
    // 可选属性
    private String couponId;
    private String deliveryAddress;
    private String remark;
    private String payType;

    // 私有化构造器
    private VendingOrder(Builder builder) {
        this.orderId = builder.orderId;
        this.machineId = builder.machineId;
        this.userId = builder.userId;
        this.productIds = builder.productIds;
        this.payAmount = builder.payAmount;
        this.couponId = builder.couponId;
        this.deliveryAddress = builder.deliveryAddress;
        this.remark = builder.remark;
        this.payType = builder.payType;

        // 校验依赖关系:配送模式必须填地址
        if ("DELIVERY".equals(payType) && (deliveryAddress == null || deliveryAddress.isEmpty())) {
            throw new IllegalArgumentException("配送模式必须填写配送地址");
        }
    }

    // 静态内部建造者
    public static class Builder {
        // 必填属性(final强制初始化)
        private final String orderId;
        private final String machineId;
        private final String userId;
        private final List<String> productIds;
        private final BigDecimal payAmount;
        // 可选属性
        private String couponId;
        private String deliveryAddress;
        private String remark;
        private String payType = "WECHAT"; // 默认微信支付

        // 构造器:强制传入必填属性
        public Builder(String orderId, String machineId, String userId, List<String> productIds, BigDecimal payAmount) {
            this.orderId = orderId;
            this.machineId = machineId;
            this.userId = userId;
            this.productIds = productIds;
            this.payAmount = payAmount;
        }

        // 链式方法
        public Builder couponId(String couponId) {
            this.couponId = couponId;
            return this;
        }

        public Builder deliveryAddress(String deliveryAddress) {
            this.deliveryAddress = deliveryAddress;
            return this;
        }

        public Builder remark(String remark) {
            this.remark = remark;
            return this;
        }

        public Builder payType(String payType) {
            this.payType = payType;
            return this;
        }

        // 构建订单
        public VendingOrder build() {
            // 基础校验
            if (productIds == null || productIds.isEmpty()) {
                throw new IllegalArgumentException("订单商品列表不能为空");
            }
            if (payAmount.compareTo(BigDecimal.ZERO) <= 0) {
                throw new IllegalArgumentException("支付金额必须大于0");
            }
            return new VendingOrder(this);
        }
    }

    @Override
    public String toString() {
        return "VendingOrder{" +
                "orderId='" + orderId + '\'' +
                ", machineId='" + machineId + '\'' +
                ", userId='" + userId + '\'' +
                ", productIds=" + productIds +
                ", payAmount=" + payAmount +
                ", couponId='" + couponId + '\'' +
                ", deliveryAddress='" + deliveryAddress + '\'' +
                ", remark='" + remark + '\'' +
                ", payType='" + payType + '\'' +
                '}';
    }
}

// 客户端使用
public class Client {
    public static void main(String[] args) {
        List<String> productIds = Arrays.asList("PROD001", "PROD002");
        // 构建配送订单(必须填配送地址)
        VendingOrder deliveryOrder = new VendingOrder.Builder(
                "ORDER001",
                "VM001",
                "USER001",
                productIds,
                new BigDecimal("99.9")
        )
        .payType("DELIVERY")
        .deliveryAddress("北京市海淀区XX街道")
        .couponId("COUPON001")
        .build();

        // 构建自提订单(无需配送地址)
        VendingOrder pickUpOrder = new VendingOrder.Builder(
                "ORDER002",
                "VM001",
                "USER001",
                productIds,
                new BigDecimal("89.9")
        )
        .payType("PICK_UP")
        .remark("请尽快自提")
        .build();

        System.out.println("配送订单:" + deliveryOrder);
        System.out.println("自提订单:" + pickUpOrder);
    }
}

七、总结

核心要点

  1. 建造者模式的核心是分离 "构建流程" 和 "具体配置",既保证构建过程标准化,又支持配置个性化;
  2. 实战中优先使用静态内部类 + 链式调用的简化版,经典版(Director+Builder)仅用于流程固定的场景;
  3. 适用场景:复杂对象(多必填 + 多可选属性)、需要控制构建顺序、避免创建不完整对象。

关键提醒

  • 不要滥用:简单对象(≤3 个属性)无需使用,直接用构造器 /setter 即可;
  • 必做校验:在build()方法中校验必填属性和属性依赖关系,避免半成品对象;
  • 简化开发:用 Lombok 的@Builder注解减少重复代码,提升开发效率。

建造者模式是业务开发中最常用的设计模式之一,掌握它能让你构建复杂对象时更优雅、更安全,避免写出 "参数爆炸" 的糟糕代码。

相关推荐
山沐与山3 小时前
【设计模式】Python工厂模式与依赖注入:FastAPI的Depends到底在干嘛
python·设计模式·fastapi
.简.简.单.单.3 小时前
Design Patterns In Modern C++ 中文版翻译 第十一章 享元模式
c++·设计模式·享元模式
BD_Marathon4 小时前
设计模式——类图
设计模式
山沐与山4 小时前
【设计模式】 Python代理模式:从入门到实战
python·设计模式·代理模式
范纹杉想快点毕业5 小时前
C语言设计模式:从基础架构到高级并发系统(完整实现版)
c语言·开发语言·设计模式
她和夏天一样热5 小时前
【实战篇】设计模式在开发中的真实应用
java·开发语言·设计模式
.简.简.单.单.5 小时前
Design Patterns In Modern C++ 中文版翻译 第十章 外观模式
c++·设计模式·外观模式
山沐与山6 小时前
【设计模式】Python状态模式:从入门到实战
python·设计模式·状态模式
BD_Marathon6 小时前
设计模式的分类
设计模式