2.3 完整演练:密码校验器的三轮循环


2.3 完整演练:密码校验器的三轮循环

本节摘要:本节把前两节拆开讲的红、绿、重构串成连续的三轮演练,完整代码逐轮给出,让密码校验器在节律里从无到有。前两节的规则在这里一条条兑现:最薄切片、验证失败原因、最小实现、每轮重构、小步提交。读完你应能照着同样的节奏独立完成自己的三轮演练。

把一句"密码要有点强度"变成能上线的代码,中间到底发生了几件事?空口回答没有说服力,跟着三轮循环走一遍,答案自己会浮出来。演练的起点是 2.1、2.2 两节已经铺好的地基:Rating 数据类与 _check_length 规则函数已经就位,测试集里躺着一条绿着的长度测试。现在,第二轮循环开工。

第二轮:数字规则

红灯先来。需求清单上的下一条是"必须包含数字",切成最薄断言:

def test_password_without_digit_is_rejected(): result = rate("abcdefghX") assert result.valid is False assert "缺少数字" in result.reasons

运行,红。仔细读失败信息:AssertionError: assert True is False——abcdefghX 有十个字符,长度规则放行了,实现给出的是 valid=True。红灯亮的原因确实是"数字规则缺失",与预期一致,红灯有效。

绿灯,最小实现。按 2.2 立的判据,只写数字规则本身:

import re def _check_digit(password: str) -> list[str]: return [] if re.search(r"\d", password) else ["缺少数字"] def rate(password: str) -> Rating: reasons = _check_length(password) reasons += _check_digit(password) return Rating(valid=not reasons, reasons=reasons)

绿了。重构窗口打开:reasons += ... 的堆叠方式刚出现两处,还谈不上重复,但第三条规则到来时就会变成三处——本轮先记下这个信号,不动手。提交,信息写 test_password_without_digit_is_rejected

第三轮:强度分级

红灯:规则齐全的密码应该拿到"强"的评级。这条测试逼出了一块新能力——分级,而不只是拒绝:

def test_password_with_all_classes_gets_strong(): result = rate("Str0ngPass") assert result.valid is True assert result.level == "strong"

先验证失败原因:Str0ngPass 长度够、有数字,所以前两条规则全放行,失败发生在 Rating 没有 level 属性——正是要新增的能力,红灯有效。

绿灯:最小实现是给 Rating 补 level 字段,并按"过了几道规则"给级:

@dataclass class Rating: valid: bool level: str = "weak" reasons: list[str] = field(default_factory=list) def rate(password: str) -> Rating: checks = [_check_length, _check_digit] reasons = [r for check in checks for r in check(password)] level = "strong" if len(checks) == len([c for c in checks if not c(password)]) else "medium" return Rating(valid=not reasons, level=level, reasons=reasons)

等等——这行 level 表达式又绕又难读,两分钟看不懂自己刚写了什么。测试还绿着,行为没问题,这正是重构的活儿:

def rate(password: str) -> Rating: checks = [_check_length, _check_digit] reasons = list(chain.from_iterable(check(password) for check in checks)) passed = len(checks) - len(reasons) level = "strong" if passed == len(checks) else "weak" return Rating(valid=not reasons, level=level, reasons=reasons)

再跑测试,绿。顺手把上一轮记下的"堆叠"信号处理掉:规则收进 checks 清单,未来加规则只是 append 一个函数。注意 level 的 medium 分支没有任何测试要求它——按最小原则,它不配存在,删掉。这就是三定律在日常里的样子:不是背出来的,是删出来的。

图:三轮循环的演进图

图:三轮循环的演进图

迁移到你自己的项目

演练的价值要在迁移中兑现。换到你手头项目时,三处替换值得先想清楚:把"密码校验"换成你域里最简单的规则密集函数(金额计算、状态判断、格式转换都是好候选);把本轮的重构手法换成你项目最缺的那种(命名烂的先练重命名,重复多的先练消除重复);把提交节奏换成团队现有粒度——节律可以完全不动,外围一切都可以本地化。给自己设个验收:独立完成三轮循环、每轮都有一次亲手验证的失败原因、至少一次重构被测试全程护住。做到这三条,第 2 章就算真正长在身上了。

演练的变式:换个顺序行不行

有人会问:三轮的顺序可以换吗?当然可以——从分级规则起步也是合法切片,只是分级依赖"规则齐全"的判定,起步就得分叉处理"没过全"的情况,红灯会红在半路。切片顺序的原则是让每轮的绿灯距离最近:先做依赖最少的规则,设计自然朝低耦合方向长。这个现象在 2.4 节会正式化为三角法;你在演练里凭直觉已经用过它了。

演练到此,循环的三种角色——红灯切需求、绿灯练克制、重构还技术债——应该都摸过一遍实物了。第 4 节补最后一块拼图:当测试从几条涨到几十条,用什么手法让它们保持表达力又不变成负担。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U