看完一个示例代码,你觉得"懂了"。到自己的项目里要用,却发现处处不对:参数不一样、异常路径没写、并发场景没考虑。而示例里这些都不存在——因为示例为了说清机制,把其它变量都删掉了。
"看得懂示例"与"能用在自己场景"之间,缺的是"把变量加回去"这一步。这个"不被当成学习动作"是根因:"看"是学习,"加回变量"被当成"干活"——于是后者不被计入"学习时间",也就不被安排。
而它往往不被当成"学习动作",于是"学了很多示例,还是不会用"。
1. 示例做了什么"删减"
| 示例保留 | 示例删掉 |
|---|---|
| 核心机制 | 错误处理 |
| 最短路径 | 并发与边界 |
| 单一输入 | 真实数据形状 |
| 命名简化 | 配置与环境 |
第二项最明显:示例里几乎不写"失败怎么办"——而生产代码里"失败路径"往往比"正常路径"更长。第二项的"成本"具体:示例 10 行、生产 60 行,多出来的 50 行里 40 行是"失败与边界"——于是"照抄示例"的结果是"没有失败处理的代码"。
第三项的"暴露时机"很晚:它要等"第一次遇到脏数据"——而那时你已经在"按示例写完"的代码里,改起来要动结构。
第三项最隐蔽:示例用"理想数据"(非空、格式正确),而真实数据"缺字段、超长、编码不一"。
2. "把变量加回去"的四步
| 步骤 | 动作 |
|---|---|
| 1 列变量 | 示例里被省略的"环境因素" |
| 2 挑 2–3 个加回 | 不要一次全加 |
| 3 改到能跑 | 在真实场景里跑通 |
| 4 记"哪里不同" | 形成"适配笔记" |
第三项是关键:"改到能跑"会逼出"你没注意到的不一样"——而"看"是看不出来的。第三项的"难点"在"跑不起来时不知道该改哪":这正是"学习发生的地方"——所以"卡住"不是失败,而是"变量被加回来了"。
第四项的"时效性"很强:差异清单在"刚搬完"时最清楚,隔一周就只剩"当时好像改了几处"——所以它要在"改完立刻写"。
第四项的产物很实用:"示例 → 我的场景"的差异清单,下次遇到同类需求直接查。
// 一份"适配笔记"的示例(把示例搬到项目后记下来)
示例:一个 @Cacheable 方法,直接返回查询结果
我的场景差异:
1 我的方法被同类内部调用 → 注解不生效(改成独立 Bean)
2 我的返回值可能为 null → 加 unless = "#result != null"
3 我的 key 有多个参数 → 显式 key = "'id:' + #id"
结论:示例的"一句话"背后,有三个"必须改"的地方
3. 三种"加回"的优先顺序
| 优先级 | 加回什么 |
|---|---|
| 高 | 错误路径(失败怎么办) |
| 高 | 输入形状(真实数据) |
| 中 | 并发(会不会同时跑) |
| 低 | 规模(数据量多大) |
第一项与第二项是"最常漏、也最容易崩"的两处第二项(输入形状)有个"便宜做法":从真实日志/库里抓 10 条数据当输入,而不是"自己造一条"——于是"缺字段、超长"这类问题当场暴露。
:"示例不写的失败路径"与"示例不用的真实数据"。
第四项可以"先按小规模跑通,再压测"——因为"规模问题"通常表现为"性能",而不是"功能错"。
4. 为什么"看示例"会给出虚假的掌握感
| 机制 | 说明 |
|---|---|
| 识别容易 | 读代码是"识别",不是"生成" |
| 删除变量 | 示例被简化到"只有一条路径" |
| 无反馈 | 不跑就不知道"哪里不对" |
第二项的"推论"是"示例越短,越要补":三行的示例通常省掉了"异常、并发、配置"——于是"看起来最容易学",实际"缺得最多"。
第二项是"示例的善意":它为了让机制清楚,故意删掉变量——而学习者会把"删掉变量后的版本"当成"完整版本"。
第三项说明"必须动手":"能跑起来"是一个客观反馈,而"觉得懂"不是。第三项的"替代"是"改一个参数,预测会发生什么,再跑":预测错了就说明"机制没真正懂"——这比"跑通"更能检验理解。
"加回变量"有一个成本很低的做法:先写"三行断言",再写实现。
// 三行断言(比"照抄示例"更能暴露差异)
1 空输入时:返回什么?(示例里没写)
2 失败时:抛什么/返回什么错误码?(示例里没写)
3 并发调用时:会不会互相影响?(示例里没写)
// 用法:三行都写不出来 → 说明"示例的变量还没加回来"
5. "适配笔记"该怎么写
| 要素 | 作用 |
|---|---|
| 场景差异(几条) | 下次复用 |
| 改动点(代码/配置) | 可复制 |
| 踩到的坑 | 避免重犯 |
| 验证方式 | 确认对 |
第二项的"格式"最好是"diff"而不是"描述":- key = "#root.args" → + key = "'id:' + #id"——它比"改一下 key 的写法"精确得多。
第二项让"笔记"变成"可执行":下次不是"读一遍",而是"抄一遍改动"。
// 适配笔记的四个字段(一张卡片)
来源:某示例(链接/章节)
差异:①②③
改动:代码 diff / 配置项
验证:跑通 + 一个断言 + 一次压测
坑:踩到的那一处(如"内部调用不生效")
"跑通"与"能用"之间还有一个"验证"动作,成本很低:
// 三步验证(每步 5 分钟)
1 用真实数据跑一次(不是自己造的)
2 故意让依赖失败(关掉下游/给错参数),看是否"如预期失败"
3 并发 10 路跑一分钟,看结果是否稳定
// 判据:三步都过 → "能用";只过第 1 步 → "能跑"
6. 排错:五种"学了不会用"
| 现象 | 成因 | 处理方向 |
|---|---|---|
| 示例都懂,一写就卡 | 没"加回变量" | 挑 2 个变量加回 |
| 抄了但跑不起来 | 环境/依赖不同 | 先"改到能跑" |
| 跑起来了但线上崩 | 没加"错误路径" | 补失败处理 |
| 每次都要重查 | 无笔记 | 写适配笔记 |
| 换个场景又不会 | 只学了"这个例子" | 找"机制"而不是"代码" |
第五项是"根":"示例"教的是"机制",而"代码"只是"机制的一种写法"——只看代码,就只学会了一种写法。
最后一个"机制"视角:示例的价值在"机制",而"代码"只是它的一个实例。
// 同一机制的三种实例(学会"机制"就能都看懂)
@Cacheable(Java 注解):靠代理拦截
functools.lru_cache(Python):靠装饰器包装
Cache::remember(PHP):靠显式调用
// 机制:读前查缓存、未命中回源、写入缓存(含 TTL 与失效)
// 判据:能否"用另一种语言/写法"重写一遍——能,才叫学会机制
7. 边界:什么情况"看示例"就够
| 场景 | 为什么 | 做法 |
|---|---|---|
| 只需"知道有这个东西" | 不打算用 | 看一眼 |
| API 用法(参数含义) | 与场景无关 | 看文档 |
| 一次性验证(跑个 demo) | 不进生产 | 照抄 |
| 已有同类代码在项目里 | 有"现成适配" | 抄项目内的 |
判断口径一句话:"这段示例会不会进我的生产代码"。最后一条与"来源"有关的边界:"官方示例"与"博客示例"的删减程度不同:前者常省略"配置与依赖",后者常省略"错误处理"。所以"缺什么"要按"来源"预判。
会,就必须"加回变量 + 记适配笔记";不会(只是了解、只是 demo),看完即可。
8. 自测
- 示例保留了什么、删掉了什么?为什么"删掉失败路径"最常见?
- "把变量加回去"的四步里,哪一步会"逼出你没注意到的不一样"?
- "加回"的优先顺序是什么?为什么"错误路径"排在高位?
- 为什么"看示例"会给出"虚假的掌握感"?"识别"与"生成"的差别是什么?
- "适配笔记"的四个字段各解决什么问题?为什么"改动点"让它可执行?
- 判断"看示例是否够"的那个问题是什么?