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

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


目录

  • [1. 模式概述](#1. 模式概述)
  • [2. 使用场景](#2. 使用场景)
  • [3. 优缺点分析](#3. 优缺点分析)
  • [4. 实际应用案例](#4. 实际应用案例)
  • [5. 结构与UML类图](#5. 结构与UML类图)
  • [6. 代码示例](#6. 代码示例)
  • [7. 测试用例](#7. 测试用例)
  • [8. 常见误区与反例](#8. 常见误区与反例)
  • [9. 最佳实践](#9. 最佳实践)
  • [10. 参考资料与延伸阅读](#10. 参考资料与延伸阅读)

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类图

plantuml 复制代码
@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 基本结构示例

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

java 复制代码
// 产品类:电脑
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 实际业务场景:订单对象建造者

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

java 复制代码
// 订单对象
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. 测试用例

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

java 复制代码
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. 参考资料与延伸阅读

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

相关推荐
huangyujun992012330 分钟前
设计模式杂谈-模板设计模式
java·设计模式
magic 2451 小时前
Java设计模式:责任链模式
java·设计模式·责任链模式
YGGP12 小时前
【结构型模式】代理模式
设计模式
庄小焱16 小时前
设计模式——中介者设计模式(行为型)
设计模式
庄小焱19 小时前
设计模式——备忘录设计模式(行为型)
设计模式
庄小焱19 小时前
设计模式——代理设计模式(结构型)
设计模式
哆啦A梦的口袋呀19 小时前
基于Python学习《Head First设计模式》第三章 装饰者模式
python·学习·设计模式
哆啦A梦的口袋呀19 小时前
基于Python学习《Head First设计模式》第五章 单件模式
python·学习·设计模式
季鸢20 小时前
Java设计模式之备忘录模式详解
java·设计模式·备忘录模式
摘星编程1 天前
工厂方法模式深度解析:从原理到应用实战
java·设计模式·软件工程·工厂方法模式