应将 setup 逻辑拆分为职责单一、输入输出明确的组合式函数,如 validate_config、load_secrets 等,并通过 setupcontext 链式组合,支持条件启用、依赖注入与可控副作用。

把复杂的 setup 逻辑封装成高内聚、可复用的组合式函数,核心不是“写得更长”,而是“拆得更准、连得更稳”。重点在于职责清晰、输入输出明确、无隐式依赖。
按职责切分,每个函数只做一件事
setup 过程通常包含校验、初始化、配置加载、资源准备、状态检查等环节。不要堆在一个函数里,而是逐层抽象:
- validate_config:只检查配置项是否存在、类型是否合法、值是否在合理范围内
- load_secrets:只负责从环境变量或 Vault 加载密钥,不处理业务逻辑
- init_database:只建立连接、执行迁移(或跳过),不读写业务数据
- setup_logging:只配置日志器并返回 logger 实例,不触发任何日志输出
用显式组合代替嵌套调用
避免写成 init_all(config) 内部顺序调用五六个步骤——这样无法跳过、无法重试、无法单测。推荐用管道式组合:
- 定义一个轻量级上下文容器,如
SetupContext(config={...}, logger=None, db=None) - 每个函数接收
SetupContext并返回更新后的实例:ctx = load_secrets(ctx) - 主流程变成清晰的链式表达:
ctx = (validate_config >> load_secrets >> init_database >> setup_logging)(initial_ctx)
支持条件启用与运行时注入
不同环境(dev/test/prod)或不同模块对 setup 的需求不同。通过高阶函数控制行为:
- 用装饰器封装通用横切逻辑:
@if_enabled("database")让init_database可开关 - 用依赖注入替代硬编码:
def init_database(db_class=SQLAlchemyDB),便于测试替换 - 允许传入回调函数处理中间状态,比如
on_error=lambda ctx: send_alert(ctx.config)
保证函数“纯”或“可控副作用”
高内聚的前提是函数行为可预测:
- 校验类函数(如
validate_config)必须是纯函数:输入相同,结果一定相同,不修改入参 - 初始化类函数(如
init_database)允许有副作用,但必须满足:只修改自己负责的资源、失败时能干净回滚、不污染全局状态 - 所有函数都应有明确返回值(成功/失败标识 + 新上下文),不靠抛异常来表示常规流程分支










