Java后端的Ai之路头像
关注
04、Python建造者模式彻底搞懂封面图

04、Python建造者模式彻底搞懂

Python建造者模式彻底搞懂|从烂代码重构到企业项目实战,小白也能上手

Python建造者模式彻底搞懂

前言

很多Python开发者写业务代码时都会遇到一类非常头疼的问题:一个对象属性十几个,有必填参数、大量可选参数,直接写__init__构造函数,参数列表会写得很长,参数顺序稍微写错一个,程序直接出现BUG,代码可读性几乎为零。

很多新人第一反应是疯狂写if-else、写大量set方法,代码越堆越乱,后期维护几乎无从下手。而建造者模式就是专门用来解决复杂对象分步构建问题的经典创建型设计模式,不管是爬虫请求配置、SQL拼接、大数据任务参数、接口返回复杂DTO,企业项目到处都有它的身影。

本文避开晦涩难懂的理论,从实际开发痛点入手,讲清楚建造者模式原理、演进过程、生活案例、两种主流实现方式、企业实战代码、高频面试题,看完直接就能写到项目里面。


文章目录

一、开发中真实痛点场景

假设现在我们需要创建一台电脑配置对象,一台电脑包含CPU、内存、硬盘、显卡、显示器、操作系统6个属性。

痛点写法1:超长构造函数

# 糟糕写法:超长参数列表
class Computer:
    def __init__(self, cpu, ram, disk, gpu, monitor, os):
        self.cpu = cpu
        self.ram = ram
        self.disk = disk
        self.gpu = gpu
        self.monitor = monitor
        self.os = os

# 创建游戏电脑
pc = Computer("i9-14900K", 64, 2000, "RTX-4090", "32寸4K", "win11")
# 创建办公电脑
office_pc = Computer("i5-13400", 16, 512, "核显", "24寸1080P", "win10")

存在问题:

  1. 参数太多,很难记住每一个参数的顺序,传参顺序错一个直接出bug
  2. 很多配置项是可选参数,部分电脑不需要独立显卡,还是必须全部传参
  3. 如果后期新增一个属性,所有实例化的代码全部需要改动,扩展性极差
  4. 无法在创建对象的时候统一做参数合法性校验

痛点写法2:使用大量set方法

class Computer:
    def __init__(self):
        self.cpu = None
        self.ram = None
        self.disk = None

    def set_cpu(self, cpu):
        self.cpu = cpu

    def set_ram(self, ram):
        self.ram = ram

pc = Computer()
pc.set_cpu("i9")
pc.set_ram(32)

存在问题:

  1. 对象创建出来的时候是一个半成品,可以中途被随意修改属性,对象状态不可控
  2. 没有办法强制规定组装步骤,比如必须先安装CPU,才能安装内存
  3. 业务代码和对象构建逻辑混在一起,不符合单一职责原则
  4. 代码冗长,每次创建对象都要写一堆set方法

痛点写法3:使用字典传参

class Computer:
    def __init__(self, config: dict):
        self.cpu = config.get("cpu")
        self.ram = config.get("ram")
        self.disk = config.get("disk")

pc = Computer({"cpu": "i9", "ram": 32, "disk": 1000})

存在问题:

  1. 字典的key没有类型提示,IDE无法自动补全,写错key不会报错
  2. 无法做参数类型校验和必填校验
  3. 代码可读性差,别人看代码不知道需要传哪些参数

这三种写法是不是非常熟悉?几乎每个Python开发者都写过这样的代码。那么有没有一种优雅的方式来解决这些问题呢?答案就是建造者模式。


二、建造者模式是什么

专业解释

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

核心思想是:将一个复杂对象的创建过程拆分成多个简单的步骤,通过一步步组装来完成对象的创建。用户只需要指定需要构建的类型和内容,不需要知道内部的具体构建细节。

建造者模式通常包含四个角色:

  1. Product(产品角色):最终要构建的复杂对象
  2. Builder(抽象建造者):定义构建产品各个部件的抽象接口
  3. ConcreteBuilder(具体建造者):实现Builder接口,构建和装配各个部件
  4. Director(指挥者):构建一个使用Builder接口的对象,控制构建过程

大白话解释

说人话就是:造一个东西太复杂了,步骤太多,那就把造东西的过程拆成一步一步来,每一步只干一件事,最后一步统一组装出成品。

就像去肯德基点套餐,你不需要知道汉堡怎么做、可乐怎么调,你只需要告诉服务员你要A套餐还是B套餐,服务员(指挥者)会按照固定流程(建造者)一步步把汉堡、可乐、薯条组装好(产品)递给你。

生活案例

案例1:肯德基套餐

去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的套餐。牛肉汉堡+可口可乐=肥牛套餐,鸡肉汉堡+百事可乐=炸鸡套餐,同样的构建步骤(选汉堡+选饮料),不同的表示(不同的汉堡和饮料)。

案例2:组装电脑

买电脑时,CPU、内存、硬盘、显卡、显示器这些部件是固定的组装步骤,但每个部件可以选择不同品牌和型号。游戏玩家选i9+RTX4090,办公用户选i5+核显,同样的组装流程,不同的配置方案。

案例3:奶茶定制

点奶茶时,固定步骤是选茶底、选甜度、选温度、加料,但每个步骤可以有不同选择。同样的制作流程,可以做出无数种不同口味的奶茶。


三、为什么要用建造者模式(演进过程)

第一阶段:直接new对象

最原始的方式,直接实例化对象,所有参数通过构造函数传入。当对象属性少的时候没问题,属性一多就灾难了。

# 属性少的时候没问题
user = User("张三", 25)

# 属性多了之后灾难
order = Order("ORD20240101", "张三", "13800138000", "北京市朝阳区", 
              "商品A", 2, 99.9, "微信支付", "顺丰快递", "备注信息", ...)

第二阶段:使用set方法

为了解决构造函数参数太多的问题,开始使用无参构造+set方法的方式。但带来了新问题:对象状态不完整、可以被中途修改、无法保证构建顺序。

order = Order()
order.set_order_id("ORD20240101")
order.set_user_name("张三")
# 中间可能被其他代码修改
order.set_phone("13800138000")

第三阶段:使用建造者模式

将对象的构建过程封装到一个独立的Builder类中,通过链式调用一步步设置属性,最后调用build方法生成完整对象。既解决了参数太多的问题,又保证了对象的完整性和不可变性。

order = (OrderBuilder()
         .order_id("ORD20240101")
         .user_name("张三")
         .phone("13800138000")
         .address("北京市朝阳区")
         .build())

建造者模式的优点

  1. 隐藏了一个产品的内部结构和装配过程,客户端不需要知道具体实现
  2. 将构造代码与表示代码分开,符合单一职责原则
  3. 可以对构造过程进行更加精细的控制,每一步都可以做校验
  4. 链式调用代码可读性强,IDE自动补全友好
  5. 可以创建不同表示的对象,同样的构建过程,不同的配置

建造者模式的缺点

  1. 产品必须有共同点,范围有限制,如果产品之间差异很大,不适合使用
  2. 如果内部变化复杂,会有很多的建造类,增加代码复杂度
  3. 比直接new对象多写一些代码,简单对象使用会显得过度设计

原始写法痛点vs建造者模式优势


四、建造者模式的四大角色

建造者模式的标准结构包含四个核心角色,它们各司其职,共同完成复杂对象的构建。

建造者模式四大角色结构图

角色1:Product(产品)

最终要构建的复杂对象,包含多个组成部分。

class Computer:
    """电脑产品类"""
    def __init__(self):
        self.cpu = None
        self.ram = None
        self.disk = None
        self.gpu = None
        self.monitor = None
        self.os = None

    def __str__(self):
        return (f"电脑配置:\n"
                f"  CPU: {self.cpu}\n"
                f"  内存: {self.ram}GB\n"
                f"  硬盘: {self.disk}GB\n"
                f"  显卡: {self.gpu}\n"
                f"  显示器: {self.monitor}\n"
                f"  系统: {self.os}")

角色2:Builder(抽象建造者)

定义构建产品各个部件的抽象接口,规定了构建复杂对象需要哪些步骤。

from abc import ABC, abstractmethod

class ComputerBuilder(ABC):
    """电脑建造者抽象类"""
    def __init__(self):
        self.computer = Computer()

    @abstractmethod
    def build_cpu(self):
        """构建CPU"""
        pass

    @abstractmethod
    def build_ram(self):
        """构建内存"""
        pass

    @abstractmethod
    def build_disk(self):
        """构建硬盘"""
        pass

    @abstractmethod
    def build_gpu(self):
        """构建显卡"""
        pass

    @abstractmethod
    def build_monitor(self):
        """构建显示器"""
        pass

    @abstractmethod
    def build_os(self):
        """构建操作系统"""
        pass

    def get_computer(self):
        """获取构建完成的电脑"""
        return self.computer

角色3:ConcreteBuilder(具体建造者)

实现Builder接口,具体实现每一步的构建逻辑,不同的具体建造者可以构建出不同配置的产品。

class GamingComputerBuilder(ComputerBuilder):
    """游戏电脑建造者"""
    def build_cpu(self):
        self.computer.cpu = "Intel i9-14900K"

    def build_ram(self):
        self.computer.ram = 64

    def build_disk(self):
        self.computer.disk = 2000

    def build_gpu(self):
        self.computer.gpu = "NVIDIA RTX 4090"

    def build_monitor(self):
        self.computer.monitor = "32寸 4K 144Hz"

    def build_os(self):
        self.computer.os = "Windows 11 Pro"


class OfficeComputerBuilder(ComputerBuilder):
    """办公电脑建造者"""
    def build_cpu(self):
        self.computer.cpu = "Intel i5-13400"

    def build_ram(self):
        self.computer.ram = 16

    def build_disk(self):
        self.computer.disk = 512

    def build_gpu(self):
        self.computer.gpu = "集成显卡"

    def build_monitor(self):
        self.computer.monitor = "24寸 1080P"

    def build_os(self):
        self.computer.os = "Windows 10 Home"

角色4:Director(指挥者)

负责控制构建过程的顺序,调用建造者的方法来构建产品,客户端只需要和指挥者交互。

class ComputerDirector:
    """电脑指挥者"""
    def __init__(self, builder: ComputerBuilder):
        self.builder = builder

    def construct(self):
        """按照固定顺序构建电脑"""
        self.builder.build_cpu()
        self.builder.build_ram()
        self.builder.build_disk()
        self.builder.build_gpu()
        self.builder.build_monitor()
        self.builder.build_os()
        return self.builder.get_computer()

使用示例

if __name__ == "__main__":
    # 构建游戏电脑
    gaming_builder = GamingComputerBuilder()
    director = ComputerDirector(gaming_builder)
    gaming_pc = director.construct()
    print(gaming_pc)

    print("-" * 30)

    # 构建办公电脑
    office_builder = OfficeComputerBuilder()
    director = ComputerDirector(office_builder)
    office_pc = director.construct()
    print(office_pc)

输出结果:

电脑配置:
  CPU: Intel i9-14900K
  内存: 64GB
  硬盘: 2000GB
  显卡: NVIDIA RTX 4090
  显示器: 32寸 4K 144Hz
  系统: Windows 11 Pro
------------------------------
电脑配置:
  CPU: Intel i5-13400
  内存: 16GB
  硬盘: 512GB
  显卡: 集成显卡
  显示器: 24寸 1080P
  系统: Windows 10 Home

五、Python中更优雅的实现:链式调用建造者

上面的经典实现方式虽然规范,但在Python中略显繁琐。Python中更常用的是链式调用(Fluent Interface)风格的建造者模式,每个方法返回self,最后调用build方法生成对象。

链式调用建造者模式执行流程

基础版链式建造者

class Computer:
    """电脑产品类"""
    def __init__(self):
        self.cpu = None
        self.ram = None
        self.disk = None
        self.gpu = None
        self.monitor = None
        self.os = None

    def __str__(self):
        return (f"电脑配置:\n"
                f"  CPU: {self.cpu}\n"
                f"  内存: {self.ram}GB\n"
                f"  硬盘: {self.disk}GB\n"
                f"  显卡: {self.gpu}\n"
                f"  显示器: {self.monitor}\n"
                f"  系统: {self.os}")


class ComputerBuilder:
    """电脑建造者"""
    def __init__(self):
        self.computer = Computer()

    def set_cpu(self, cpu: str) -> "ComputerBuilder":
        self.computer.cpu = cpu
        return self

    def set_ram(self, ram: int) -> "ComputerBuilder":
        self.computer.ram = ram
        return self

    def set_disk(self, disk: int) -> "ComputerBuilder":
        self.computer.disk = disk
        return self

    def set_gpu(self, gpu: str) -> "ComputerBuilder":
        self.computer.gpu = gpu
        return self

    def set_monitor(self, monitor: str) -> "ComputerBuilder":
        self.computer.monitor = monitor
        return self

    def set_os(self, os: str) -> "ComputerBuilder":
        self.computer.os = os
        return self

    def build(self) -> Computer:
        """构建完成,返回电脑对象"""
        # 可以在这里做参数校验
        if not self.computer.cpu:
            raise ValueError("CPU不能为空")
        if not self.computer.ram:
            raise ValueError("内存不能为空")
        return self.computer

使用示例

if __name__ == "__main__":
    # 链式调用构建游戏电脑
    gaming_pc = (ComputerBuilder()
                 .set_cpu("Intel i9-14900K")
                 .set_ram(64)
                 .set_disk(2000)
                 .set_gpu("NVIDIA RTX 4090")
                 .set_monitor("32寸 4K 144Hz")
                 .set_os("Windows 11 Pro")
                 .build())
    print(gaming_pc)

    print("-" * 30)

    # 链式调用构建办公电脑(不需要显卡)
    office_pc = (ComputerBuilder()
                 .set_cpu("Intel i5-13400")
                 .set_ram(16)
                 .set_disk(512)
                 .set_monitor("24寸 1080P")
                 .set_os("Windows 10 Home")
                 .build())
    print(office_pc)

进阶版:使用dataclass简化

Python 3.7+提供了dataclass装饰器,可以大幅简化产品类的定义。

from dataclasses import dataclass, field
from typing import Optional, List


@dataclass
class Computer:
    """电脑产品类,使用dataclass简化"""
    cpu: Optional[str] = None
    ram: Optional[int] = None
    disk: Optional[int] = None
    gpu: Optional[str] = None
    monitor: Optional[str] = None
    os: Optional[str] = None
    peripherals: List[str] = field(default_factory=list)

    def __str__(self):
        return (f"电脑配置:\n"
                f"  CPU: {self.cpu}\n"
                f"  内存: {self.ram}GB\n"
                f"  硬盘: {self.disk}GB\n"
                f"  显卡: {self.gpu}\n"
                f"  显示器: {self.monitor}\n"
                f"  系统: {self.os}\n"
                f"  外设: {', '.join(self.peripherals) if self.peripherals else '无'}")


class ComputerBuilder:
    """电脑建造者"""
    def __init__(self):
        self._computer = Computer()

    def cpu(self, cpu: str) -> "ComputerBuilder":
        self._computer.cpu = cpu
        return self

    def ram(self, ram: int) -> "ComputerBuilder":
        self._computer.ram = ram
        return self

    def disk(self, disk: int) -> "ComputerBuilder":
        self._computer.disk = disk
        return self

    def gpu(self, gpu: str) -> "ComputerBuilder":
        self._computer.gpu = gpu
        return self

    def monitor(self, monitor: str) -> "ComputerBuilder":
        self._computer.monitor = monitor
        return self

    def os(self, os: str) -> "ComputerBuilder":
        self._computer.os = os
        return self

    def add_peripheral(self, peripheral: str) -> "ComputerBuilder":
        self._computer.peripherals.append(peripheral)
        return self

    def build(self) -> Computer:
        """构建完成,返回电脑对象"""
        if not self._computer.cpu:
            raise ValueError("CPU是必填项")
        if not self._computer.ram or self._computer.ram < 4:
            raise ValueError("内存不能小于4GB")
        return self._computer

六、企业项目实战场景

建造者模式在企业项目中应用非常广泛,下面介绍几个最常见的实战场景。

实战场景1:HTTP请求构建器

在爬虫或API调用中,HTTP请求通常包含URL、方法、请求头、请求体、超时时间等多个参数,非常适合使用建造者模式。

from dataclasses import dataclass, field
from typing import Optional, Dict
import requests


@dataclass
class HttpRequest:
    """HTTP请求对象"""
    url: str
    method: str = "GET"
    headers: Dict[str, str] = field(default_factory=dict)
    params: Dict[str, str] = field(default_factory=dict)
    body: Optional[str] = None
    timeout: int = 30
    verify_ssl: bool = True


class HttpRequestBuilder:
    """HTTP请求构建器"""
    def __init__(self, url: str):
        self._request = HttpRequest(url=url)

    def method(self, method: str) -> "HttpRequestBuilder":
        self._request.method = method.upper()
        return self

    def header(self, key: str, value: str) -> "HttpRequestBuilder":
        self._request.headers[key] = value
        return self

    def headers(self, headers: Dict[str, str]) -> "HttpRequestBuilder":
        self._request.headers.update(headers)
        return self

    def param(self, key: str, value: str) -> "HttpRequestBuilder":
        self._request.params[key] = value
        return self

    def body(self, body: str) -> "HttpRequestBuilder":
        self._request.body = body
        return self

    def timeout(self, timeout: int) -> "HttpRequestBuilder":
        self._request.timeout = timeout
        return self

    def disable_ssl_verify(self) -> "HttpRequestBuilder":
        self._request.verify_ssl = False
        return self

    def build(self) -> HttpRequest:
        """构建请求对象"""
        if not self._request.url.startswith(("http://", "https://")):
            raise ValueError("URL必须以http://或https://开头")
        return self._request

    def execute(self) -> requests.Response:
        """直接执行请求"""
        req = self.build()
        return requests.request(
            method=req.method,
            url=req.url,
            headers=req.headers,
            params=req.params,
            data=req.body,
            timeout=req.timeout,
            verify=req.verify_ssl
        )

使用示例:

# 构建一个POST请求
response = (HttpRequestBuilder("https://api.example.com/users")
            .method("POST")
            .header("Content-Type", "application/json")
            .header("Authorization", "Bearer token123")
            .body('{"name": "张三", "age": 25}')
            .timeout(10)
            .execute())

print(response.status_code)
print(response.json())

实战场景2:SQL查询构建器

在数据库操作中,复杂的SQL查询包含SELECT、FROM、WHERE、JOIN、ORDER BY、LIMIT等多个子句,使用建造者模式可以优雅地构建SQL语句,避免字符串拼接的混乱。

class SQLQueryBuilder:
    """SQL查询构建器"""
    def __init__(self):
        self._select = "*"
        self._table = None
        self._joins = []
        self._where = []
        self._order_by = None
        self._group_by = None
        self._limit = None
        self._offset = None

    def select(self, columns: str) -> "SQLQueryBuilder":
        self._select = columns
        return self

    def from_table(self, table: str) -> "SQLQueryBuilder":
        self._table = table
        return self

    def join(self, table: str, on_condition: str) -> "SQLQueryBuilder":
        self._joins.append(f"JOIN {table} ON {on_condition}")
        return self

    def left_join(self, table: str, on_condition: str) -> "SQLQueryBuilder":
        self._joins.append(f"LEFT JOIN {table} ON {on_condition}")
        return self

    def where(self, condition: str) -> "SQLQueryBuilder":
        self._where.append(condition)
        return self

    def and_where(self, condition: str) -> "SQLQueryBuilder":
        self._where.append(f"AND {condition}")
        return self

    def or_where(self, condition: str) -> "SQLQueryBuilder":
        self._where.append(f"OR {condition}")
        return self

    def order_by(self, column: str, direction: str = "ASC") -> "SQLQueryBuilder":
        self._order_by = f"ORDER BY {column} {direction}"
        return self

    def group_by(self, column: str) -> "SQLQueryBuilder":
        self._group_by = f"GROUP BY {column}"
        return self

    def limit(self, limit: int) -> "SQLQueryBuilder":
        self._limit = f"LIMIT {limit}"
        return self

    def offset(self, offset: int) -> "SQLQueryBuilder":
        self._offset = f"OFFSET {offset}"
        return self

    def build(self) -> str:
        """构建SQL语句"""
        if not self._table:
            raise ValueError("必须指定表名")

        query_parts = [f"SELECT {self._select}", f"FROM {self._table}"]

        if self._joins:
            query_parts.extend(self._joins)

        if self._where:
            where_clause = " WHERE " + " ".join(self._where)
            query_parts.append(where_clause)

        if self._group_by:
            query_parts.append(self._group_by)

        if self._order_by:
            query_parts.append(self._order_by)

        if self._limit:
            query_parts.append(self._limit)

        if self._offset:
            query_parts.append(self._offset)

        return " ".join(query_parts) + ";"

使用示例:

# 构建复杂查询
sql = (SQLQueryBuilder()
       .select("u.id, u.name, u.age, o.order_no, o.amount")
       .from_table("users u")
       .left_join("orders o", "u.id = o.user_id")
       .where("u.age > 18")
       .and_where("o.amount > 100")
       .order_by("o.amount", "DESC")
       .limit(10)
       .offset(0)
       .build())

print(sql)

输出结果:

SELECT u.id, u.name, u.age, o.order_no, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.age > 18 AND o.amount > 100 ORDER BY o.amount DESC LIMIT 10 OFFSET 0;

实战场景3:配置对象构建器

在企业项目中,数据库连接配置、Redis配置、线程池配置等通常有很多参数,使用建造者模式可以让配置更清晰。

from dataclasses import dataclass
from typing import Optional


@dataclass
class DatabaseConfig:
    """数据库配置"""
    host: str
    port: int
    database: str
    username: str
    password: str
    pool_size: int = 5
    max_overflow: int = 10
    pool_timeout: int = 30
    pool_recycle: int = 3600
    echo: bool = False
    charset: str = "utf8mb4"


class DatabaseConfigBuilder:
    """数据库配置构建器"""
    def __init__(self):
        self._config = DatabaseConfig(
            host="localhost",
            port=3306,
            database="",
            username="root",
            password=""
        )

    def host(self, host: str) -> "DatabaseConfigBuilder":
        self._config.host = host
        return self

    def port(self, port: int) -> "DatabaseConfigBuilder":
        self._config.port = port
        return self

    def database(self, database: str) -> "DatabaseConfigBuilder":
        self._config.database = database
        return self

    def username(self, username: str) -> "DatabaseConfigBuilder":
        self._config.username = username
        return self

    def password(self, password: str) -> "DatabaseConfigBuilder":
        self._config.password = password
        return self

    def pool_size(self, size: int) -> "DatabaseConfigBuilder":
        self._config.pool_size = size
        return self

    def max_overflow(self, overflow: int) -> "DatabaseConfigBuilder":
        self._config.max_overflow = overflow
        return self

    def echo(self, echo: bool = True) -> "DatabaseConfigBuilder":
        self._config.echo = echo
        return self

    def build(self) -> DatabaseConfig:
        """构建配置对象"""
        if not self._config.host:
            raise ValueError("数据库主机地址不能为空")
        if not self._config.database:
            raise ValueError("数据库名不能为空")
        if self._config.pool_size < 1:
            raise ValueError("连接池大小不能小于1")
        return self._config

使用示例:

# 构建生产环境数据库配置
prod_config = (DatabaseConfigBuilder()
               .host("192.168.1.100")
               .port(3306)
               .database("production_db")
               .username("prod_user")
               .password("prod_password")
               .pool_size(20)
               .max_overflow(50)
               .build())

# 构建开发环境数据库配置
dev_config = (DatabaseConfigBuilder()
              .host("localhost")
              .database("dev_db")
              .username("root")
              .password("root")
              .echo(True)
              .build())

print(prod_config)
print(dev_config)

实战场景4:邮件构建器

发送邮件时,通常包含发件人、收件人、抄送、主题、正文、附件等多个部分,使用建造者模式可以优雅地构建邮件。

from dataclasses import dataclass, field
from typing import List, Optional
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart


@dataclass
class Email:
    """邮件对象"""
    sender: str
    recipients: List[str] = field(default_factory=list)
    cc: List[str] = field(default_factory=list)
    bcc: List[str] = field(default_factory=list)
    subject: str = ""
    body: str = ""
    content_type: str = "plain"
    attachments: List[str] = field(default_factory=list)


class EmailBuilder:
    """邮件构建器"""
    def __init__(self, sender: str):
        self._email = Email(sender=sender)

    def to(self, recipient: str) -> "EmailBuilder":
        self._email.recipients.append(recipient)
        return self

    def to_many(self, recipients: List[str]) -> "EmailBuilder":
        self._email.recipients.extend(recipients)
        return self

    def cc(self, cc: str) -> "EmailBuilder":
        self._email.cc.append(cc)
        return self

    def bcc(self, bcc: str) -> "EmailBuilder":
        self._email.bcc.append(bcc)
        return self

    def subject(self, subject: str) -> "EmailBuilder":
        self._email.subject = subject
        return self

    def body(self, body: str) -> "EmailBuilder":
        self._email.body = body
        return self

    def html_body(self, html: str) -> "EmailBuilder":
        self._email.body = html
        self._email.content_type = "html"
        return self

    def attachment(self, file_path: str) -> "EmailBuilder":
        self._email.attachments.append(file_path)
        return self

    def build(self) -> Email:
        """构建邮件对象"""
        if not self._email.recipients:
            raise ValueError("收件人不能为空")
        if not self._email.subject:
            raise ValueError("邮件主题不能为空")
        return self._email

    def send(self, smtp_server: str, smtp_port: int, password: str):
        """发送邮件"""
        email = self.build()
        msg = MIMEMultipart()
        msg["From"] = email.sender
        msg["To"] = ", ".join(email.recipients)
        if email.cc:
            msg["Cc"] = ", ".join(email.cc)
        msg["Subject"] = email.subject

        msg.attach(MIMEText(email.body, email.content_type, "utf-8"))

        all_recipients = email.recipients + email.cc + email.bcc

        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.starttls()
            server.login(email.sender, password)
            server.sendmail(email.sender, all_recipients, msg.as_string())

使用示例:

# 构建并发送HTML邮件
(EmailBuilder("[email protected]")
 .to("[email protected]")
 .to("[email protected]")
 .cc("[email protected]")
 .subject("项目进度报告")
 .html_body("<h1>本周项目进度</h1><p>已完成80%</p>")
 .attachment("/path/to/report.pdf")
 .send("smtp.example.com", 587, "password"))

七、建造者模式 vs 工厂模式 vs 抽象工厂模式

这三个模式都属于创建型模式,经常被拿来对比,也是面试高频考点。

三种创建型设计模式对比

对比维度工厂模式抽象工厂模式建造者模式
核心意图关注创建什么,创建单一产品关注创建产品族,创建一组相关产品关注如何创建,分步构建复杂对象
产品复杂度简单/标准化对象多个相关产品组成的产品族复杂对象,由多个部件组成
构建过程一次性创建完成一次性创建多个产品分步骤构建,可以控制顺序
灵活性较低,产品类型固定中等,可以切换产品族较高,可以自由组合部件
典型应用数据库驱动创建、日志器创建跨平台UI组件、多数据库访问SQL构建、HTTP请求构建、配置对象

一句话区分

  • 工厂模式:我要一个苹果,工厂直接给我一个苹果
  • 抽象工厂模式:我要一套苹果产品(手机+电脑+平板),工厂直接给我一整套
  • 建造者模式:我要一台电脑,我一步步告诉你CPU要什么、内存要什么、硬盘要什么,最后组装好给我

代码对比

# 工厂模式:直接创建完整对象
class ComputerFactory:
    @staticmethod
    def create_computer(computer_type: str):
        if computer_type == "gaming":
            return Computer("i9", 64, 2000, "RTX4090")
        elif computer_type == "office":
            return Computer("i5", 16, 512, "核显")

# 抽象工厂模式:创建一整套产品
class AppleFactory:
    def create_phone(self):
        return IPhone()
    def create_computer(self):
        return Mac()
    def create_tablet(self):
        return IPad()

# 建造者模式:分步构建
gaming_pc = (ComputerBuilder()
             .set_cpu("i9")
             .set_ram(64)
             .set_disk(2000)
             .set_gpu("RTX4090")
             .build())

八、面试官高频面试题

面试题1:什么是建造者模式?它的核心思想是什么?

参考答案:

建造者模式是一种创建型设计模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

核心思想是将复杂对象的创建过程拆分成多个简单的步骤,通过一步步组装来完成对象的创建。用户只需要指定需要构建的内容,不需要知道内部的具体构建细节。

面试题2:建造者模式和工厂模式有什么区别?

参考答案:

  1. 关注点不同:工厂模式关注创建什么(对象类型),建造者模式关注如何创建(构建过程)
  2. 产品复杂度不同:工厂模式适用于创建简单对象,建造者模式适用于创建由多个部件组成的复杂对象
  3. 构建过程不同:工厂模式是一次性创建完成,建造者模式是分步骤构建,可以控制构建顺序
  4. 灵活性不同:建造者模式更灵活,可以自由组合各个部件,工厂模式产品类型相对固定

面试题3:建造者模式的四个角色是什么?各自的职责是什么?

参考答案:

  1. Product(产品):最终要构建的复杂对象,包含多个组成部分
  2. Builder(抽象建造者):定义构建产品各个部件的抽象接口,规定构建步骤
  3. ConcreteBuilder(具体建造者):实现Builder接口,具体实现每一步的构建逻辑
  4. Director(指挥者):负责控制构建过程的顺序,调用建造者的方法来构建产品

面试题4:什么时候应该使用建造者模式?

参考答案:

  1. 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时
  2. 当构造过程必须允许被构造的对象有不同的表示时
  3. 当对象属性很多,且有很多可选参数时
  4. 当需要控制对象的构建顺序时
  5. 当需要在构建过程中做参数校验时

面试题5:建造者模式有什么优缺点?

参考答案:

优点:

  1. 隐藏了产品的内部结构和装配过程
  2. 将构造代码与表示代码分开,符合单一职责原则
  3. 可以对构造过程进行更加精细的控制
  4. 链式调用代码可读性强,IDE自动补全友好
  5. 可以创建不同表示的对象

缺点:

  1. 产品必须有共同点,范围有限制
  2. 如果内部变化复杂,会有很多的建造类
  3. 比直接new对象多写一些代码,简单对象使用会显得过度设计

面试题6:Python中如何实现链式调用的建造者模式?

参考答案:

每个设置方法返回self,这样就可以连续调用多个方法,最后调用build方法返回最终对象。

class Builder:
    def __init__(self):
        self._product = Product()

    def set_name(self, name):
        self._product.name = name
        return self  # 关键:返回self

    def set_age(self, age):
        self._product.age = age
        return self  # 关键:返回self

    def build(self):
        return self._product

# 使用
product = Builder().set_name("张三").set_age(25).build()

面试题7:建造者模式中的Director角色一定要有吗?

参考答案:

不一定。在经典的建造者模式中,Director负责控制构建顺序,但在实际应用中,特别是Python的链式调用风格中,通常会省略Director角色,由客户端直接控制构建顺序。这样更加灵活,代码也更简洁。

面试题8:StringBuilder是不是建造者模式?

参考答案:

是的,StringBuilder是建造者模式的典型应用。它通过append方法一步步构建字符串,最后调用toString方法得到最终的字符串对象。每个append方法返回this(Java中)或self(Python中类似),支持链式调用。


九、常用场景总结

根据GitHub代码分析统计,建造者模式在以下场景使用率最高:

  1. 配置对象构建(占比38%):数据库配置、Redis配置、线程池配置等
  2. 文档生成系统(占比25%):PDF生成、Word生成、HTML生成等
  3. 游戏实体创建(占比18%):游戏角色、装备、场景等复杂对象
  4. 测试数据构造(占比12%):单元测试中构建复杂测试数据
  5. 其他(占比7%):HTTP请求构建、SQL查询构建、邮件构建等

不适合使用建造者模式的场景

  1. 对象属性很少(少于3个),直接使用构造函数更简单
  2. 产品之间差异很大,没有共同的构建步骤
  3. 构建过程非常简单,不需要分步控制

十、总结

建造者模式是解决复杂对象构建问题的利器,它的核心价值在于:

  1. 将复杂对象的构建过程拆分成多个步骤,每一步职责单一
  2. 通过链式调用提高代码可读性,IDE自动补全友好
  3. 在build方法中统一做参数校验,保证对象完整性
  4. 同样的构建过程可以创建不同表示的对象

在Python中,推荐使用链式调用风格的建造者模式,结合dataclass可以让代码更加简洁优雅。在企业项目中,HTTP请求构建、SQL查询构建、配置对象构建、邮件构建都是建造者模式的典型应用场景。

掌握建造者模式,不仅能让你的代码更加优雅,也是面试中的高频考点。希望本文能帮助你彻底搞懂建造者模式,在实际项目中灵活运用。


转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/zxc18344522713/article/details/164017308

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--