方法命名的完整语义模型:前置定语 · 动词 · 介词短语
方法名不是描述实现,而是提前暴露风险与边界的工程语言。
1. 回到最初的问题:为什么“动词规范”还不够?
在工程实践中,我曾系统性梳理过一个核心问题:
不同层级(Controller / AppService / Domain / Infra)
应该使用哪些动词,分别代表什么语义边界?
这解决了一个关键痛点:
👉 动词失真会直接导致架构腐化。
但在长期实践中,我逐渐意识到:
仅靠动词,仍然不足以完整表达一个方法的真实语义。
2. 一个更完整的结论
在现代工程中,一个可被长期遵守的方法命名,应当同时回答三件事:
- 它做什么 → 动词(Verb)
- 它指向哪里 / 为了谁 / 使用什么 → 介词(Preposition)
- 它处于什么阶段或策略下 → 前置定语(Qualifier)
因此,一个稳定的方法命名模型应为:
前置定语 + 动词 + 介词短语
3. 动词:语义承诺的核心(仍然是第一位)
动词依然是方法命名中最重要的部分,因为它直接承诺了:
- 是否修改状态
- 是否产生副作用
- 是否允许 IO / 远程调用
3.1 最小可用动词集(推荐白名单)
① 纯规则 / 无副作用(Domain 优先)
| 动词 | 语义 |
|---|---|
| validate | 校验,失败抛异常 |
| check | 判断,返回 boolean |
| calculate | 计算 |
| derive | 派生 |
| parse | 解析 |
| match | 匹配 |
❌ 禁止 IO / 禁止状态修改
② 状态演进(Domain / AppService)
| 动词 | 语义 |
|---|---|
| apply | 状态演进 |
| change | 明确修改 |
| mark | 标记 |
| reset | 重置 |
| assign | 建立关系 |
③ 用例编排(仅 AppService)
| 动词 | 语义 |
|---|---|
| execute | 执行一个业务用例 |
| dispatch | 分发 |
| orchestrate | 多步骤编排 |
| trigger | 触发异步 |
execute只允许存在于 AppService
④ IO / 边界行为(Infra)
| 动词 | 语义 |
|---|---|
| call | 外部接口 |
| fetch | 读取 |
| persist | 持久化 |
| send | 发送 |
| publish | 发布事件 |
4. 介词:补充“方向性”,而不是补救动词
介词的作用只有一个:
在动词语义已成立的前提下,补充“指向性信息”。
4.1 允许使用的介词(严格收敛)
to —— 目标 / 外部指向
1 | syncOrderToExternalSystem(); |
语义承诺:
-
存在明确目标
-
多用于跨系统、跨边界
for —— 用例 / 服务对象
1 | calculateFeeForOrder(); |
强调:为了谁 / 为了什么用例
with —— 手段 / 策略 / 上下文
1 | executePaymentWithRetry(); |
限制:
-
with后必须是具体策略名词 -
禁止抽象占位词(Something / Logic)
4.2 明确禁止的介词
-
by -
via -
using -
through
这些词会让方法名退化成“自然语言解释”,而非工程语义。
5. 前置定语:表达阶段,而不是叠加信息
前置定语用于表达:
-
执行模型
-
生命周期阶段
-
批量 / 内部语义
5.1 前置定语白名单
| 前置定语 | 语义 |
|---|---|
| async | 异步 |
| sync | 同步 |
| batch | 批量 |
| internal | 内部 |
| before / after | 生命周期钩子 |
强规则:
-
❗ 最多只能有一个
-
❗ 不得掩盖动词本身的语义
6. 前置 / 后置处理的正确姿势
6.1 仅表达时序(弱语义)
1 | beforeExecute(); |
仅适用于模板方法 / 框架钩子。
6.2 用动词直接表达语义(推荐)
1 | validateOrder(); |
能不用 pre / post,就不要用。
7. 分层方法命名最终规范(收敛版)
7.1 Controller —— 表达用户意图
1 | createOrder(); |
❌ 禁止:
- execute / apply / sync
7.2 AppService —— 用例编排者
1 | executeCreateOrder(); |
7.3 Domain —— 规则与状态核心
1 | validateOrder(); |
❌ 禁止:
- call / persist / send
7.4 Infra —— 外部世界边界
1 | callLxApi(); |
8. 综合示例(完整语义拆解)
1 | asyncSyncOutboundOrderToLxForRetry(); |
拆解:
-
async:执行模型 -
sync:行为类型 -
OutboundOrder:核心对象 -
toLx:外部目标 -
forRetry:用例归属
不读实现,也能判断副作用与风险等级。
9. 速记 / 速查表
一句话口诀
先选动词,再想介词,最后加定语
动词三问法
-
改不改状态? →
apply / change -
调不调外部? →
call / send -
只是规则? →
validate / calculate
介词速记
-
去哪里 →
to -
为了谁 →
for -
用什么 →
with
定语速记
-
执行模型 →
async / sync -
规模 →
batch -
生命周期 →
before / after
10. 结语
方法命名不是个人偏好,而是:
团队长期协作中的语义契约。
当一个方法名能让你在 Code Review 中
不用点进去,也能判断它“值不值得警惕”,
它才是一个合格的工程命名。