设计模式系列(07):建造者模式(Builder)

发布于:2025-06-04 ⋅ 阅读:(25) ⋅ 点赞:(0)

本文为设计模式系列第7篇,聚焦创建型模式中的建造者模式,涵盖定义、原理、实际业务场景、优缺点、最佳实践及详细代码示例,适合系统学习与实战应用。


目录

1. 模式概述

建造者模式(Builder Pattern)是一种创建型设计模式。它将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。适用于需要分步骤、可定制、部件复杂的对象创建。

1.1 定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

1.2 目的

  • 分离构建过程与表示,支持灵活扩展
  • 简化复杂对象的创建,隐藏细节
  • 支持多种产品配置和复用

2. 使用场景

建造者模式在实际开发中应用广泛,常见场景包括:

  1. 复杂对象组装:如汽车、电脑、房屋等多部件组装,步骤复杂且可定制。
  2. 文档/报表生成:如HTML、PDF、Word等多格式文档的分步生成。
  3. UI表单/界面构建:如动态表单、复杂界面分步渲染。
  4. 配置对象创建:如系统配置、网络配置等参数众多且组合灵活的对象。

真实业务背景举例:

  • 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。
  • SaaS平台支持多种报表格式导出,建造者模式可统一流程、灵活扩展。
  • 游戏角色装备、技能、属性等复杂对象,建造者模式便于分步构建和扩展。

3. 优缺点分析

3.1 优点

  1. 封装性:隐藏构建细节,客户端无需关心内部实现。
  2. 灵活性:支持不同产品配置和多种表示。
  3. 可维护性:构建过程独立,便于扩展和维护。

3.2 缺点

  1. 类数量增加:每种产品/部件都需建造者类,结构复杂。
  2. 适用范围有限:只适用于部件复杂、构建过程稳定的对象。
  3. 构建过程固定:如步骤变化大,需频繁修改建造者接口。

4. 实际应用案例

  1. 订单对象组装:商品、收货、支付、优惠等分步组装。
  2. 报表/文档生成:多格式报表、文档的统一生成流程。
  3. 汽车/电脑组装:多部件、可选配置的复杂对象。
  4. UI表单构建:动态表单、复杂界面分步渲染。

5. 结构与UML类图

@startuml
package "Builder Pattern" #DDDDDD {
  class Computer {
    + setCpu(cpu: String): void
    + setRam(ram: String): void
    + setStorage(storage: String): void
    + setGpu(gpu: String): void
  }
  interface ComputerBuilder {
    + buildCpu(): void
    + buildRam(): void
    + buildStorage(): void
    + buildGpu(): void
    + getResult(): Computer
  }
  class GamingComputerBuilder implements ComputerBuilder
  class OfficeComputerBuilder implements ComputerBuilder
  class ComputerEngineer {
    - builder: ComputerBuilder
    + constructComputer(): void
  }
  ComputerBuilder <|.. GamingComputerBuilder
  ComputerBuilder <|.. OfficeComputerBuilder
  ComputerEngineer o-- ComputerBuilder : builder
  GamingComputerBuilder --> Computer : getResult()
  OfficeComputerBuilder --> Computer : getResult()
}
@enduml

6. 代码示例

6.1 基本结构示例

业务背景: 电脑组装业务,支持多种配置,分步构建。

// 产品类:电脑
package com.example.patterns.builder;

import java.util.Objects;
import java.util.StringJoiner;

public class Computer {
    private String cpu;
    private String ram;
    private String storage;
    private String gpu;
    
    public void setCpu(String cpu) { 
        this.cpu = Objects.requireNonNull(cpu, "CPU cannot be null"); 
    }
    public void setRam(String ram) { 
        this.ram = Objects.requireNonNull(ram, "RAM cannot be null"); 
    }
    public void setStorage(String storage) { 
        this.storage = Objects.requireNonNull(storage, "Storage cannot be null"); 
    }
    public void setGpu(String gpu) { 
        this.gpu = Objects.requireNonNull(gpu, "GPU cannot be null"); 
    }
    
    // 改进的toString方法
    @Override
    public String toString() {
        return new StringJoiner(", ", "Computer{", "}")
                .add("cpu='" + cpu + "'")
                .add("ram='" + ram + "'")
                .add("storage='" + storage + "'")
                .add("gpu='" + gpu + "'")
                .toString();
    }
    
    // 添加equals和hashCode方法,便于测试
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Computer computer = (Computer) o;
        return Objects.equals(cpu, computer.cpu) &&
               Objects.equals(ram, computer.ram) &&
               Objects.equals(storage, computer.storage) &&
               Objects.equals(gpu, computer.gpu);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(cpu, ram, storage, gpu);
    }
}

// 抽象建造者
public interface ComputerBuilder {
    void buildCpu();
    void buildRam();
    void buildStorage();
    void buildGpu();
    Computer getResult();
    
    // 添加重置方法,支持builder复用
    default void reset() {
        // 子类可以重写此方法实现重置逻辑
    }
}

// 游戏电脑建造者
public class GamingComputerBuilder implements ComputerBuilder {
    private Computer computer = new Computer();
    
    @Override
    public void buildCpu() { computer.setCpu("Intel i9-13900K"); }
    @Override
    public void buildRam() { computer.setRam("32GB DDR5"); }
    @Override
    public void buildStorage() { computer.setStorage("2TB NVMe SSD"); }
    @Override
    public void buildGpu() { computer.setGpu("NVIDIA RTX 4090"); }
    @Override
    public Computer getResult() { return computer; }
    
    @Override
    public void reset() {
        computer = new Computer();
    }
}

// 办公电脑建造者
public class OfficeComputerBuilder implements ComputerBuilder {
    private Computer computer = new Computer();
    
    @Override
    public void buildCpu() { computer.setCpu("Intel i5-13600K"); }
    @Override
    public void buildRam() { computer.setRam("16GB DDR4"); }
    @Override
    public void buildStorage() { computer.setStorage("1TB SATA SSD"); }
    @Override
    public void buildGpu() { computer.setGpu("Intel UHD Graphics"); }
    @Override
    public Computer getResult() { return computer; }
    
    @Override
    public void reset() {
        computer = new Computer();
    }
}

// 组装工程师(导演类)
public class ComputerEngineer {
    private ComputerBuilder builder;
    
    public ComputerEngineer(ComputerBuilder builder) { 
        this.builder = Objects.requireNonNull(builder, "Builder cannot be null"); 
    }
    
    public void constructComputer() {
        builder.reset(); // 确保从干净状态开始
        builder.buildCpu();
        builder.buildRam();
        builder.buildStorage();
        builder.buildGpu();
    }
    
    // 添加部分构建方法,支持定制化
    public void constructBasicComputer() {
        builder.reset();
        builder.buildCpu();
        builder.buildRam();
        builder.buildStorage();
        // 不添加独显
    }
}

// 客户端示例
public class Client {
    public static void main(String[] args) {
        try {
            // 创建游戏电脑
            ComputerBuilder gamingBuilder = new GamingComputerBuilder();
            ComputerEngineer gamingEngineer = new ComputerEngineer(gamingBuilder);
            gamingEngineer.constructComputer();
            Computer gamingComputer = gamingBuilder.getResult();
            System.out.println("Gaming Computer: " + gamingComputer);
            
            // 创建办公电脑
            ComputerBuilder officeBuilder = new OfficeComputerBuilder();
            ComputerEngineer officeEngineer = new ComputerEngineer(officeBuilder);
            officeEngineer.constructComputer();
            Computer officeComputer = officeBuilder.getResult();
            System.out.println("Office Computer: " + officeComputer);
            
        } catch (Exception e) {
            System.err.println("Failed to build computer: " + e.getMessage());
        }
    }
    // 总结:通过建造者模式,客户端可灵活组装不同配置电脑,便于扩展和维护。
}

6.2 实际业务场景:订单对象建造者

业务背景: 电商平台订单对象包含商品、收货、支付、优惠等多个可选部件,建造者模式可灵活组装。

// 订单对象
package com.example.patterns.builder.order;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.StringJoiner;

public class Order {
    private String product;
    private String address;
    private String payment;
    private String discount;
    private BigDecimal amount; // 添加金额字段
    
    public void setProduct(String product) { this.product = product; }
    public void setAddress(String address) { this.address = address; }
    public void setPayment(String payment) { this.payment = payment; }
    public void setDiscount(String discount) { this.discount = discount; }
    public void setAmount(BigDecimal amount) { this.amount = amount; }
    
    public String getProduct() { return product; }
    public String getAddress() { return address; }
    public String getPayment() { return payment; }
    public String getDiscount() { return discount; }
    public BigDecimal getAmount() { return amount; }
    
    // 校验订单是否有效
    public boolean isValid() {
        return product != null && !product.trim().isEmpty() &&
               address != null && !address.trim().isEmpty() &&
               payment != null && !payment.trim().isEmpty() &&
               amount != null && amount.compareTo(BigDecimal.ZERO) > 0;
    }
    
    @Override
    public String toString() {
        return new StringJoiner(", ", "Order{", "}")
                .add("product='" + product + "'")
                .add("address='" + address + "'")
                .add("payment='" + payment + "'")
                .add("discount='" + discount + "'")
                .add("amount=" + amount)
                .toString();
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Order order = (Order) o;
        return Objects.equals(product, order.product) &&
               Objects.equals(address, order.address) &&
               Objects.equals(payment, order.payment) &&
               Objects.equals(discount, order.discount) &&
               Objects.equals(amount, order.amount);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(product, address, payment, discount, amount);
    }
}

// 订单建造者
public class OrderBuilder {
    private Order order = new Order();
    
    public OrderBuilder product(String product) { 
        if (product == null || product.trim().isEmpty()) {
            throw new IllegalArgumentException("Product cannot be null or empty");
        }
        order.setProduct(product); 
        return this; 
    }
    
    public OrderBuilder address(String address) { 
        if (address == null || address.trim().isEmpty()) {
            throw new IllegalArgumentException("Address cannot be null or empty");
        }
        order.setAddress(address); 
        return this; 
    }
    
    public OrderBuilder payment(String payment) { 
        if (payment == null || payment.trim().isEmpty()) {
            throw new IllegalArgumentException("Payment method cannot be null or empty");
        }
        order.setPayment(payment); 
        return this; 
    }
    
    public OrderBuilder discount(String discount) { 
        // 折扣是可选的,允许null
        order.setDiscount(discount); 
        return this; 
    }
    
    public OrderBuilder amount(BigDecimal amount) {
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        order.setAmount(amount);
        return this;
    }
    
    // 便捷方法,接受double类型
    public OrderBuilder amount(double amount) {
        return amount(BigDecimal.valueOf(amount));
    }
    
    public Order build() { 
        if (!order.isValid()) {
            throw new IllegalStateException("Order is not valid. Missing required fields.");
        }
        return order; 
    }
    
    // 重置建造者,支持复用
    public OrderBuilder reset() {
        order = new Order();
        return this;
    }
}

// 客户端示例
public class OrderClient {
    public static void main(String[] args) {
        try {
            // 创建完整订单
            Order order = new OrderBuilder()
                    .product("iPhone 15 Pro")
                    .address("上海市浦东新区")
                    .payment("微信支付")
                    .discount("满1000减100")
                    .amount(8999.00)
                    .build();
            System.out.println("订单信息: " + order);
            
            // 创建无折扣订单
            Order simpleOrder = new OrderBuilder()
                    .product("MacBook Pro")
                    .address("北京市朝阳区")
                    .payment("支付宝")
                    .amount(15999.00)
                    .build();
            System.out.println("简单订单: " + simpleOrder);
            
        } catch (IllegalArgumentException | IllegalStateException e) {
            System.err.println("订单创建失败: " + e.getMessage());
        }
    }
    // 总结:通过链式建造者和参数校验,订单对象组装更安全,代码简洁易维护。
}

7. 测试用例

业务背景: 验证电脑建造者和订单建造者的核心功能与多实现切换。

public class BuilderPatternTest {
    
    @Test
    public void testGamingComputer() {
        ComputerBuilder builder = new GamingComputerBuilder();
        ComputerEngineer engineer = new ComputerEngineer(builder);
        // 构建游戏电脑
        engineer.constructComputer();
        Computer computer = builder.getResult();
        
        // 验证游戏电脑配置
        String computerStr = computer.toString();
        assertTrue(computerStr.contains("Intel i9-13900K"));
        assertTrue(computerStr.contains("32GB DDR5"));
        assertTrue(computerStr.contains("NVIDIA RTX 4090"));
    }
    
    @Test
    public void testOfficeComputer() {
        ComputerBuilder builder = new OfficeComputerBuilder();
        ComputerEngineer engineer = new ComputerEngineer(builder);
        // 构建办公电脑
        engineer.constructComputer();
        Computer computer = builder.getResult();
        
        // 验证办公电脑配置
        String computerStr = computer.toString();
        assertTrue(computerStr.contains("Intel i5-13600K"));
        assertTrue(computerStr.contains("16GB DDR4"));
        assertTrue(computerStr.contains("Intel UHD Graphics"));
    }
    
    @Test
    public void testOrderBuilder() {
        // 测试链式建造者
        Order order = new OrderBuilder()
                .product("iPhone 15 Pro")
                .address("上海市浦东新区")
                .payment("微信支付")
                .discount("满1000减100")
                .amount(8999.00)
                .build();
                
        assertEquals("iPhone 15 Pro", order.getProduct());
        assertEquals("微信支付", order.getPayment());
        assertTrue(order.isValid());
    }
    
    @Test
    public void testDifferentBuilders() {
        // 测试不同建造者产生不同结果
        ComputerBuilder gamingBuilder = new GamingComputerBuilder();
        ComputerBuilder officeBuilder = new OfficeComputerBuilder();
        
        ComputerEngineer engineer1 = new ComputerEngineer(gamingBuilder);
        ComputerEngineer engineer2 = new ComputerEngineer(officeBuilder);
        
        engineer1.constructComputer();
        engineer2.constructComputer();
        
        Computer gamingComputer = gamingBuilder.getResult();
        Computer officeComputer = officeBuilder.getResult();
        
        // 验证两台电脑配置不同
        assertNotEquals(gamingComputer.toString(), officeComputer.toString());
    }
}

8. 常见误区与反例

  • 误区1 :导演类(Director)过度复杂。
    如构建逻辑过多应拆分,避免导演类膨胀。
  • 误区2 :建造者接口设计不清晰。
    应保证步骤明确、职责单一。
  • 反例 :客户端直接操作产品部件。
    应通过建造者接口组装,避免高耦合。

9. 最佳实践

  1. 接口设计 :建造者接口要简洁,步骤明确,便于扩展和维护。
  2. 工厂与建造者结合 :复杂对象可结合工厂方法和建造者模式,提升灵活性。
  3. 链式调用 :对于参数较多的对象,推荐链式建造者,提升可读性。
  4. 异常与校验 :建造过程应妥善处理参数校验和异常。
  5. 文档和UML同步 :保持文档、UML和代码示例一致,便于团队协作。

10. 参考资料与延伸阅读

  • 《设计模式:可复用面向对象软件的基础》GoF
  • Effective Java(中文版)
  • https://refactoringguru.cn/design-patterns/builder
  • https://www.baeldung.com/java-builder-pattern

本文为设计模式系列第7篇,后续每篇将聚焦一个设计模式或设计原则,深入讲解实现与应用,敬请关注。


网站公告

今日签到

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