Java设计模式-建造者模式

发布于:2025-08-15 ⋅ 阅读:(23) ⋅ 点赞:(0)

Java设计模式-建造者模式

模式概述

建造者模式简介

定义

它允许逐步构建复杂对象而不至于构造函数变得庞大和难以管理。

将复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。通过定义一个独立的“建造者”角色,将对象的构造逻辑封装起来,客户端只需调用建造者的方法分步构建对象,最终生成完整的产品。

模式类型:创建型模式

作用

  • 解耦对象的构建过程与最终表示,提高代码的可维护性和扩展性;
  • 灵活支持复杂对象的多种配置组合(如可选参数的动态设置);
  • 避免“伸缩构造函数”问题(Telescoping Constructor Problem),即当对象属性过多时,构造方法参数列表过长导致的可读性差和错误风险;
  • 支持生成不可变对象(Immutable Object),通过建造者完成所有属性设置后,对象状态不再修改。

典型应用场景

  • 对象包含多个组成部分,且需要灵活组合不同配置(如汽车的发动机、底盘、座椅等部件);
  • 对象需要支持多种可选参数(如HTTP请求的URL、方法、头信息、超时时间等);
  • 需要生成不可变对象(如Java中的StringLocalDateTime,或自定义的配置类);
  • 构建过程需要统一的流程控制(如必须按顺序初始化某些属性)。

我认为:建造者模式就像一个“产品组装流水线”,通过标准化的步骤组装零件(属性),最终产出不同配置的“产品”(对象),既保证了流程的规范性,又提供了灵活的组合方式。

课程目标

  • 理解建造者模式的核心思想和经典应用场景
  • 识别应用场景,使用建造者模式解决功能要求
  • 了解建造者模式的优缺点

核心组件

角色-职责表

角色 职责 示例类名
Director 使用Builder接口来构建产品,控制对象的构建流程。在某些场景下可以省略,特别是在简单的建造者模式应用中。 ComputerDirector
Builder 定义用于创建各个部分的方法,确保必填参数被设置,并允许设置可选参数。 Computer.Builder
ConcreteBuilder 提供特定实现以构建并装配产品的具体部分。负责初始化对象的不同部分,并提供一个方法返回最终组装好的对象。 DefaultBuilder
Product 被构建的复杂对象,即通过建造者模式最终生成的对象。该对象通常包含多个属性,其中一些可能是必需的,而另一些是可选的。 Computer

类图

Director
+construct()
Builder
+buildCpu(cpu: String)
+buildMemory(memory: String)
+buildDisk(disk: String?)
+build()
ConcreteBuilder
-cpu: String
-memory: String
-disk: String
+buildCpu(cpu: String)
+buildMemory(memory: String)
+buildDisk(disk: String?)
+build()
Product
+cpu: String
+memory: String
+disk: String

传统实现 VS 建造者模式

案例需求

案例背景:构建一台电脑(Computer),需要设置CPU、内存、硬盘三个核心属性。其中,CPU和内存为必填项,硬盘为可选(默认值为512GB SSD)。

传统实现(痛点版)

代码实现

// 传统写法:通过构造方法和Setter构建对象
public class Computer {
    private final String cpu;    // 必填
    private final String memory; // 必填
    private String disk;         // 可选(默认512GB SSD)

    // 构造方法1:仅必填参数
    public Computer(String cpu, String memory) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = "512GB SSD"; // 默认值
    }

    // 构造方法2:必填+可选参数(参数列表膨胀)
    public Computer(String cpu, String memory, String disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }

    // Setter方法(可能导致对象状态不一致)
    public void setDisk(String disk) {
        this.disk = disk;
    }

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

// 客户端使用
public class Client {
    public static void main(String[] args) {
        // 场景1:使用必填参数+默认硬盘
        Computer officePC = new Computer("Intel i5", "16GB");
        
        // 场景2:自定义硬盘(需显式调用构造方法)
        Computer gamingPC = new Computer("AMD R9", "32GB", "2TB HDD");
        
        // 风险:若忘记设置必填参数(如cpu为null),对象状态非法
        // Computer invalidPC = new Computer(null, "16GB"); 
        
        // 风险:通过Setter修改必填参数(破坏不可变性)
        officePC.setDisk("1TB SSD"); 
    }
}

痛点总结

  • 参数列表膨胀:当可选参数增多时(如新增显卡、电源等),需要定义大量重载构造方法,可读性差且易出错;
  • 状态不一致:通过Setter方法设置可选参数时,可能遗漏必填参数(如cpunull),或错误修改已设置的必填参数(破坏对象不可变性);
  • 构建逻辑分散:对象的构建过程(如必填参数校验、默认值设置)分散在构造方法和Setter中,难以统一维护。

建造者模式 实现(优雅版)

代码实现

// 1. 产品类(Computer)
public class Computer {
    private final String cpu;    // 必填
    private final String memory; // 必填
    private final String disk;   // 可选

    // 私有构造方法,仅允许建造者创建对象
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.disk = builder.disk;
    }

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

    // 2. 抽象建造者(Builder接口)
    public interface Builder {
        Builder buildCpu(String cpu);    // 必填参数
        Builder buildMemory(String memory); // 必填参数
        Builder buildDisk(String disk);    // 可选参数
        Computer build(); // 最终构建产品
    }

    // 3. 具体建造者(实现Builder接口)
    public static class DefaultBuilder implements Builder {
        private String cpu;    // 必填
        private String memory; // 必填
        private String disk = "512GB SSD"; // 可选(默认值)

        @Override
        public Builder buildCpu(String cpu) {
            if (cpu == null || cpu.isEmpty()) {
                throw new IllegalArgumentException("CPU不能为空");
            }
            this.cpu = cpu;
            return this; // 返回this支持链式调用
        }

        @Override
        public Builder buildMemory(String memory) {
            if (memory == null || memory.isEmpty()) {
                throw new IllegalArgumentException("内存不能为空");
            }
            this.memory = memory;
            return this;
        }

        @Override
        public Builder buildDisk(String disk) {
            if (disk != null && !disk.isEmpty()) {
                this.disk = disk;
            }
            return this;
        }

        @Override
        public Computer build() {
            // 校验必填参数是否设置
            if (cpu == null || memory == null) {
                throw new IllegalStateException("CPU和内存为必填参数");
            }
            return new Computer(this);
        }
    }

    // 4. 指挥者(可选,用于控制标准构建流程)
    public static class Director {
        private final Builder builder;

        public Director(Builder builder) {
            this.builder = builder;
        }

        // 标准构建流程:先设置CPU和内存,再设置硬盘(可选)
        public Computer constructStandardComputer(String cpu, String memory) {
            return builder.buildCpu(cpu)
                          .buildMemory(memory)
                          .buildDisk() // 使用默认硬盘
                          .build();
        }

        public Computer constructGamingComputer(String cpu, String memory, String disk) {
            return builder.buildCpu(cpu)
                          .buildMemory(memory)
                          .buildDisk(disk)
                          .build();
        }
    }

    // 客户端使用
    public static void main(String[] args) {
        // 创建建造者
        Builder builder = new DefaultBuilder();

        // 方式1:直接通过建造者链式调用(无需指挥者)
        Computer officePC = builder.buildCpu("Intel i5")
                                   .buildMemory("16GB")
                                   .buildDisk() // 使用默认值
                                   .build();

        // 方式2:通过指挥者执行标准流程
        Director director = new Director(builder);
        Computer gamingPC = director.constructGamingComputer("AMD R9", "32GB", "2TB HDD");

        System.out.println(officePC); // Computer{cpu='Intel i5', memory='16GB', disk='512GB SSD'}
        System.out.println(gamingPC); // Computer{cpu='AMD R9', memory='32GB', disk='2TB HDD'}
    }
}

优势

  • 参数安全:必填参数通过建造者的方法强制设置(如buildCpu()校验非空),避免无效对象;
  • 灵活组合:可选参数通过方法链式调用(buildDisk())设置,默认值封装在建造者内部,客户端无需关心;
  • 不可变性支持:产品对象的属性通过私有构造方法初始化后不可修改(final修饰),线程安全;
  • 流程可控:指挥者可定义标准构建流程(如必须先设置CPU和内存),避免构建步骤混乱;
  • 代码整洁:构建逻辑集中在建造者中,客户端只需调用简洁的链式方法,可读性高。

局限

  • 类数量增加:需要定义产品类、抽象建造者、具体建造者和可能的指挥者,若产品简单(如仅1-2个属性),会导致过度设计;
  • 学习成本:对于新手开发者,需要理解建造者模式的角色分工,增加学习曲线。

模式变体

  • 简化版(无指挥者):客户端直接调用建造者的方法完成构建,省略指挥者角色(如上述案例中的DefaultBuilder直接被客户端调用);
  • 链式调用:建造者的方法返回this,支持流畅的方法链(如builder.buildCpu().buildMemory().build());
  • 不可变产品:产品对象的所有属性通过构造方法初始化后设为final,且不提供setter方法,确保线程安全;
  • Lombok @Builder:通过Lombok注解自动生成建造者代码(如@Builder(toBuilder = true)),减少手动编写样板代码;
  • 多阶段构建:建造者支持分阶段构建(如先构建基础属性,再构建高级属性),适用于复杂对象的渐进式初始化。

最佳实践

建议
优先用于多属性/可选参数对象 当对象包含3个及以上属性(尤其是可选参数)时,建造者模式能显著提升代码可读性和可维护性。
必填参数在构造方法中校验 建造者的方法(如buildCpu())应对必填参数进行非空/格式校验,避免无效对象被创建。
使用链式调用提升流畅度 建造者方法返回this,支持builder.method1().method2()的链式调用,代码更简洁。
产品属性设为**final** 确保产品对象在build()后不可修改,支持线程安全和不可变对象特性。
指挥者封装标准流程 若构建过程有固定顺序(如必须先初始化数据库连接再配置查询参数),通过指挥者统一控制流程。
避免过度设计 若对象仅需1-2个简单属性,直接使用构造方法或Setter更合适,无需引入建造者模式。

一句话总结

建造者模式通过分离对象的构建过程与表示,解决了复杂对象创建时的参数管理和灵活性问题,使代码更易维护、扩展,并支持生成安全的不可变对象。

如果关注Java设计模式内容,可以查阅作者的其他Java设计模式系列文章。😊


网站公告

今日签到

点亮在社区的每一天
去签到