Python 中的 Builder 模式实践 —— 以 UserProfileBuilder 为例
一. 背景
在开发中,我们经常需要创建复杂对象(DTO,配置,请求体等)
这些对象属性很多,但是并不是每次都需要全部填写:
- 有些属性可以用默认值
- 有些属性可以随机生成
- 有些属性依赖其他对象
如果直接用构造函数,代码会像下面这样,可读性比较差
profile = UserProfile(username="alice",email="alice@example.com",role="admin",tags=["tester"],colors=["red"],preferences=[]
)
Bulider模式可以帮我们解决这个问题
二. 什么是 Builder 模式?
1、Builder模式(构造者模式)是一种创建型设计模式
- 把“任何一步步创建对象”的逻辑抽离到一个Builder类里;
- 让调用方用链式方法逐步设置属性;
- 支持默认值、随机值、依赖值等灵活逻辑。
2、优点:
- 可读性强
- 灵活性高
- 方便扩展和默认值处理
三、代码示例
1、BuliderBase提供通用的create方法:
from typing import Any, Type, TypeVarT = TypeVar("T", bound="BuilderBase")class BuilderBase:@classmethoddef create(cls: Type[T], **kwargs) -> Any:"""通用创建方法:根据 kwargs 自动调用 with_xxx 方法或直接赋值。"""builder = cls()for key, value in kwargs.items():method_name = f"with_{key}"if hasattr(builder, method_name):getattr(builder, method_name)(value)else:setattr(builder, key, value)return builder.build_object()
2、UserProFileBulider:具体实现
import random
from typing import List, Optionalclass UserProfileBuilder(BuilderBase):def __init__(self):self.username: Optional[str] = Noneself.email: Optional[str] = Noneself.role: Optional[str] = Noneself.tags: List[str] = []self.colors: List[str] = []self.preferences: List[str] = []def enable_randomized_attributes(self, enabled: bool):"""开启随机属性生成"""if enabled:self.with_random_attributes()return selfdef with_random_attributes(self, number: Optional[int] = None):"""随机生成标签和颜色"""color_pool = ["red", "blue", "green", "yellow"]tag_pool = ["admin", "tester", "developer", "guest"]max_count = min(len(color_pool), len(tag_pool))if number is None:number = random.randint(1, max_count)count = min(number, max_count)self.colors = random.sample(color_pool, k=count)self.tags = random.sample(tag_pool, k=count)return selfdef build_object(self):"""构建 UserProfile 对象"""if self.username is None:self.username = "default_user"return UserProfile(username=self.username,email=self.email,role=self.role,tags=list(self.tags),colors=list(self.colors),preferences=list(self.preferences),)class UserProfile:"""最终要构建的对象"""def __init__(self, username, email, role, tags, colors, preferences):self.username = usernameself.email = emailself.role = roleself.tags = tagsself.colors = colorsself.preferences = preferencesdef __repr__(self):return f"<UserProfile {self.username}, {self.role}>"
3、使用示例
# 方式一:一步完成
profile = UserProfileBuilder.create(username="alice",email="alice@example.com",role="admin",enable_randomized_attributes=True
)# 方式二:链式调用
builder = UserProfileBuilder()
profile2 = (builder.with_random_attributes(2).enable_randomized_attributes(True).build_object()
)print(profile)
print(profile2)
4、优点
可读性强:链式 API 一看就懂。
灵活性高:可自由组合不同的
with_xxx
方法。默认值处理:例如用户名为空时自动填充
default_user
。可扩展:新增属性只需加对应的
with_xxx
方法。
5、缺点
对于简单对象,Builder 显得“多余”;
初学者可能不习惯
with_xxx
的用法;增加了一层抽象。
6、适用场景
测试数据构造(自动化测试用例、随机测试数据)
复杂配置对象(数据库配置、UI 配置、HTTP 请求体)
需要灵活扩展的 DTO
7、传参说明
- 普通写法
class UserProfile:def __init__(self, username, email, role):self.username = usernameself.email = emailself.role = roleprofile = UserProfile("alice", "alice@example.com", "admin")
username -> __init__ -> self.username
- builder模式写法:参数不会直接传给最终的产品类,而是先存放在Builder的属性里(比如:self._username),等到调用build()时,再把这些参数传给UserProfile,由产品类来保存:
流程:username -> builder._username -> build() -> UserProfile(self.username)
- 关键区别
普通类:参数直接进构造函数,马上存到属性上。
Builder:参数先存到 Builder,等你确认完毕,再一次性传给最终类(更灵活)。
也就是说,
self.username
、self.email
这些属性依然存在于 最终的产品类(UserProfile
)里。
只是 Builder 先帮你暂存参数,再把它们“打包”交给产品类。