尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

12岁小学生重构代码:低龄编程中的代码可读性训练

12岁小学生重构代码:低龄编程中的代码可读性训练 重构代码这四个字放在12岁小学生身上天然自带话题感。很多人第一反应是怀疑一个小学刚毕业的孩子语法都没完全学明白谈什么重构但如果你真的看过少儿编程课上的修改记录会发现这个说法并不夸张只是它和职业开发者理解的重构不在同一个层级。12岁小学生的重构代码大多不是把项目推翻重来而是把自己或同学写的一段二三十行代码改得更清楚、更好读、更容易讲给别人听。真正值得拆开看的是这些改动里暴露的思维方式一个孩子怎么理解变量名、函数、重复代码以及为什么要把代码改好。这篇文章不预设某个具体案例而是基于低龄编程学习的常见场景把小学生重构代码时的高频改动、典型代码变化、常见误区和可操作的引导流程完整拆一遍。1. 12岁孩子眼里的重构代码和你想的其实不一样1.1 先纠正一个普遍误解一个12岁孩子说我在重构代码大概率不会去考虑依赖注入、设计模式、架构分层这些内容。他更可能做的是把代码里看不懂的变量名改掉把重复写了好几遍的同一段逻辑收拢到一起把绕来绕去的判断条件重新排一下顺序或者干脆给代码加上空行和注释。这些动作放在职业开发里叫重构放在低龄编程学习里本质上是让代码重新变得可读。重构在软件工程里有一个相对严格的定义在不改变代码外部行为的前提下对内部结构进行调整。对小学生来说这个定义里的外部行为不变恰恰是最难理解也最难做到的。孩子往往会顺手把某个功能也改了或者把输入输出格式改了这在外人看来就已经算功能变更不算严格意义上的重构。所以讨论低龄重构不用把定义锁死更值得关注的是孩子有没有形成这种意识改结构比改功能更考验细心程度所以要先确认原来的功能没被改坏。1.2 低龄重构通常发生在哪些场景里从常见的编程学习路径来看孩子在小学高年级接触到的重构一般出现在下面几种场景自己写完一段代码跑通之后发现太乱想整理一下再交给老师。老师给出几段旧代码要求在不改变运行结果的前提下改得更好。和同学互相看代码发现对方的代码难读提出修改意见。学习项目越来越大从几十行扩展到两三百行原来的写法已经撑不住。这些场景有一个共同点代码量不大通常只有十到几十行。也正是因为代码量不大孩子才有机会完成一次完整的读代码、发现问题、动手修改、运行验证循环。在低龄阶段完成这个循环比修改本身更重要。很多成年人写了好几年代码都没有真正体验过通读一段代码之后有计划地动手修改的完整流程。1.3 图形化编程和文本编程里的重构差别还有一个容易被忽视的差异用图形化编程的孩子和用Python、C这类文本语言的孩子做的重构完全不是一回事。在图形化编程环境里孩子改的是积木块。他们最常见的重构动作是把一个巨大的积木堆拆成几个小积木块然后重复使用给积木块重新命名把散落的背景设置、变量初始化整理到同一个位置。这些动作同样是调整结构、保持结果不变只是没有代码文本的观感家长和老师不容易一眼看出孩子在重构。在文本编程里重构动作更接近成年人熟悉的样子改变量名、提取函数、调整缩进、删除重复代码。文本代码有个好处就是修改痕迹一目了然前后对比非常直观。我的经验是孩子一旦进入文本编程阶段就可以开始有意识地安排重构练习因为这个阶段的每一次改动都可以被记录、被比较、被复盘。2. 翻看这类代码修改记录最常见的五类改动如果把低龄学习者提交的重构代码放在一起对比会发现高频改动非常集中。不是高深的设计模式而是五类非常基础的结构性调整。2.1 把a、b、c改成能读懂的名字这是出现频率最高的一类改动。初学编程的时候很多孩子为了少打字喜欢把变量名写成a、b、c、x、y、z。到了重构阶段第一个改的就是这个。比如原来写a int(input(请输入第一条边的长度)) b int(input(请输入第二条边的长度)) c int(input(请输入第三条边的长度))重构后可能变成side_a int(input(请输入第一条边的长度)) side_b int(input(请输入第二条边的长度)) side_c int(input(请输入第三条边的长度))这个改动看起来非常基础但背后对应的是变量名要表达含义这条工程原则。对一个12岁孩子来说能主动把side放进变量名里说明他已经开始站在代码阅读者的角度思考问题而不再只是站在电脑的角度思考。2.2 把重复出现的代码抽成函数第二个高频改动是把重复的代码块抽成函数。很多初学编程的孩子在写多个类似任务时会选择复制粘贴然后稍微改一下数字。比如连续计算几组数据的和就会写好几遍几乎相同的循环。total1 0 for x in [12, 8, 15]: total1 total1 x print(total1) total2 0 for x in [7, 9, 6, 10]: total2 total2 x print(total2)重构后孩子会把这段逻辑提取成一个函数然后调用两次def total_of(numbers): total 0 for x in numbers: total total x return total print(total_of([12, 8, 15])) print(total_of([7, 9, 6, 10]))这类改动最大的价值在于孩子第一次亲身体会到写一次、用多次这个概念。他不是从书本上背下来函数可以复用而是真的遇到这段代码我写烦了写几遍太累的真实感受之后才主动去做提取。这个动机比任何教学讲解都有效。2.3 把多层嵌套条件改平小学高年级孩子写判断条件的时候特别容易出现多层嵌套尤其是那种如果...那么...否则如果...那么...的连环条件。嵌套一旦超过两层代码不仅不好看缩进层级也容易出错。score int(input(分数)) if score 60: if score 90: print(优秀) else: print(及格) else: print(不及格)重构时常见做法是把嵌套条件拆开或者用逻辑运算符合并。上面的嵌套版本可以改平成if score 90: print(优秀) elif score 60: print(及格) else: print(不及格)这个改动的核心不是让代码少几行而是让判断路径更直观。对孩子来说从外到内一层层看缩进和从上到下一行行读条件后者的理解成本明显更低。2.4 消灭魔法数字魔法数字指的是直接写在代码里、没有任何解释的神秘数字。比如成绩判断里的60、90又比如游戏里代表移动方向的1、2、3、4。孩子重构时往往不会主动去管这些数字看得懂的老师才会引导他们发现如果换一个游戏地图大小这些数字散落得到处都是改起来会非常麻烦。if key 1: player_x player_x - 1 elif key 2: player_x player_x 1重构时可以把方向数字提成有名字的常量MOVE_LEFT 1 MOVE_RIGHT 2 if key MOVE_LEFT: player_x player_x - 1 elif key MOVE_RIGHT: player_x player_x 1对12岁孩子来说这一步最大的收获不是代码变得规范而是意识到代码里出现的每一个数字都是有原因的都值得被明确表达出来。2.5 补注释、拆空行、理顺输出最后一类改动最不起眼但对低龄学习者来说非常重要给代码加上注释、用空行把不同功能模块隔开、把print输出格式统一。这些不是软件工程里的硬性重构动作却是一个孩子开始把代码当作给人看的东西的标志。在少儿编程里这个标志比代码质量本身更重要。下面用一个表格把这几类改动汇总一下改动类型具体表现背后对应的能力变量重命名a变成side_a、score语义表达提取函数重复循环收敛成一次定义抽象与复用扁平化条件嵌套if改成elif逻辑梳理魔法数字常量化60、90变成PASS、EXCELLENT参数意识注释与排版空行、注释、统一输出面向读者3. 还原一个典型改动从能跑到能给别人读为了说清楚上面五类改动具体长什么样下面准备一个非常典型的低龄重构示例。这类代码在入门班和学校信息课上很常见读取一个分数输出对应的等级。3.1 重构前一段典型的初学代码s int(input(分数)) if s 90: print(优秀) elif s 80: print(良好) elif s 70: print(中等) elif s 60: print(及格) else: print(不及格)这段代码能跑结果也对。但它有几个低龄学习者常见的问题变量s没有任何含义别人不知道它代表什么。数字90、80、70、60直接写在条件里改等级标准时要到每一行去改。输入、计算、输出全堆在一个流程里没办法单独测输入分数得到等级这一部分。没有注释过几天再看自己也不知道为什么是90不是95。3.2 重构后发生了什么变化经过一次完整的重构练习代码可能变成这样EXCELLENT 90 GOOD 80 MEDIUM 70 PASS 60 def get_grade(score): if score EXCELLENT: return 优秀 if score GOOD: return 良好 if score MEDIUM: return 中等 if score PASS: return 及格 return 不及格 def main(): score int(input(请输入你的分数)) grade get_grade(score) print(f你的等级是{grade}) main()同样一段逻辑改动前后的差异非常明显。重构后的版本做了四件核心事情把神秘数字提成常量、把等级判断封装成函数、把主流程放进main函数、把变量名从s改成score。这些动作正好对应前面说的五类常见改动中的四类。3.3 每处改动背后的判断逻辑这里要特别说明一件事让函数返回等级字符串而不是直接在函数里print其实已经超出了严格意义的重构。因为调用方式变了输出行为从函数内部打印变成了函数返回结果由主流程打印。这在软件工程里通常叫调整接口设计比重构的范畴更大。对小学生来说这个概念不需要专门讲但做练习时最好让孩子明白你在改结构的同时也改了代码的使用方式因此更要多跑几次确认结果没变。注意孩子第一次做这种练习时最容易犯的错就是把函数从直接print改成return然后忘了改调用处的代码。每一处改动后面都要紧跟一次运行验证先确认原行为没丢再谈结构变好。为什么把常量提出来因为如果及格线改成65分使用常量版本只需要改PASS60这一行而原有版本要改六个地方还容易漏改。这个只需要改一行的体验孩子一旦真实验证过就再也不想回到到处写数字的写法。为什么用main函数包起来因为很多孩子学编程的时候代码是从第一行一路往下执行的写到最后自己都不知道哪个变量在哪一步被用到。有了main函数程序从哪里开始、先做什么、后做什么一目了然。对12岁孩子而言这是一种把流程画出来的方式不需要大讲特讲作用域和函数栈的问题。4. 实操中最容易翻车的三个点看孩子做重构最容易出现的不是不会改而是改完反而更糟。这种情况非常常见不是孩子笨而是重构这件事有几个天然陷阱。4.1 追求一步到位结果越改越复杂孩子改代码有一个习惯想一次性把所有不满意的地方全改完。改完变量名之后觉得函数也该拆拆完函数觉得数值也该提成常量提完常量又觉得注释要补。结果一次改动量巨大任何一步出错都很难定位。遇到这种情况建议让孩子一次只改一类问题。第一次只改变量名跑一次第二次再抽函数再跑一次第三次整理常量和注释再跑一次。每次改动范围小出错了很容易发现。这其实就是职业开发者常用的小步重构原则只是不需要让孩子背那个名词只需要让他感受小步改、频繁验的过程。4.2 没有跑一遍再改的习惯很多孩子拿到代码的第一反应是直接开改根本没先运行一遍原代码不知道原代码在正常输入下应该输出什么。改完之后运行发现结果不对但说不清是自己的问题还是原来代码本身就有问题。正确顺序应该是先运行一遍旧代码记录正常输入下的输出结果改完一处立刻运行对比输出是否一致。这个习惯比代码本身重要得多。低龄孩子一旦养成改前先备份、改后马上验的习惯后面的学习会顺利很多。我会在练习开始前把原代码的运行输出写在一张纸条上改完一步对照一次对不上就先停。4.3 把改接口当成重构低龄孩子重构时最容易出现的认知混淆就是一边调整内部结构一边偷偷改掉原有的交互方式。比如原来代码是直接执行、打印结果孩子改成了让用户输入之后再打印或者原来函数接收三个参数孩子改成接收一个列表。程序功能看起来差不多但使用方式已经变了。严格来说这已经不是重构而是功能变更或界面变更。对老师来说这不是需要批评的错误。相反这是一个很好的教学节点。可以借机给孩子讲清楚重构的前提是外面看起来完全没变里面变好了。如果外面也变了就要先说明我改变了使用方式再继续改。这个意识一旦建立孩子对接口这个概念就有了最早期的直觉。5. 家长和老师可以参考的引导流程如果家里或班上正好有一个12岁左右、正在学编程的孩子想让他体验一次完整重构可以参考下面这套流程。不需要特殊工具只需要一段现成的代码和一个能运行代码的环境。5.1 五步练习法第一步选代码。挑一段十到三十行、已经能运行的旧代码。可以是孩子自己以前写的也可以是老师提供的样例。代码不要太大否则孩子光读懂就要花很多时间。第二步记录基准结果。运行一遍把输入和输出记录下来。这一步不要省略它是后面所有修改的对照标准。第三步找不舒服的地方。让孩子通读代码找出至少三个让他觉得不舒服的地方。找不到的话可以提示他看几个方向变量名能不能看懂、有没有重复代码、数字是不是直接写在逻辑里、有没有多余的嵌套。第四步一次改一处。每次修改只处理一个不舒服的地方改完立刻运行对比基准结果。第五步让别人读一遍。改完之后找另一个同学或者家长让对方不看原有代码只看新代码然后复述这段程序是干什么的。如果对方两分钟之内能说出来这次重构就算成功。步骤做什么验收标准选代码找一段10到30行且能运行的旧代码孩子能一两分钟讲完功能记录基准运行一遍记录输入和输出有明确的对照结果找问题找出至少3个不舒服的地方每个问题都能说清缘由一次改一处只改一类问题并运行验证输出和基准保持一致讲给别人让同学或家长读新代码并复述两分钟内能说明白5.2 用能不能讲给同学听当验收标准对于12岁孩子不要用什么可维护性、可扩展性、耦合度这些词去验收。最直观的验收标准就是你能不能把这段代码讲给同学听让对方在没有你解释的情况下读懂它。如果对方听完还要问这个变量是什么意思这个函数到底在干什么那就说明还有改进空间。这个标准特别适合低龄学习者因为它不依赖任何抽象概念只依赖给别人讲明白这种孩子本能就有的表达冲动。我会让孩子假设自己是个小老师要把这段代码讲给一个完全没看过的人听。一旦他进入讲解者的角色很多结构问题自己就暴露出来了。5.3 什么时候可以引入测试概念重构做到第三次、第四次的时候孩子通常会有一个疑问我怎么知道改完没有改错这时候就可以顺理成章地引入最基础的测试概念不需要用单元测试框架只需要写几个固定的输入预先把应该得到的输出列出来每改完一次手动跑一遍即可。如果孩子已经能熟练使用if语句和列表甚至可以让他写一个简单的自动对比脚本把输入列表和期望输出列表放进去程序自动判断重构前后的运行结果是否一致。这一步一旦完成他实际上已经理解了回归测试的核心思想。对孩子来说这比背十遍测试的定义都有用。6. 怎么判断一次重构练习真的学会了低龄编程学习有一个特点孩子经常会做但说不清。所以判断一次重构练习是否有效不能只看最终代码是否漂亮更不能看改动行数是否够多。要看得更细一些。6.1 不是看改动量而是看能不能说清原因一次优秀的孩子重构可能只改了三个变量名和一个函数结构改动不大。但只要孩子能说清楚我为什么这么改原来哪里不好改完之后哪里变好了这次练习就是有效的。反过来如果代码被改得面目全非但孩子说不出任何理由那大概率是模仿了某个范例并没有真正理解。所以在练习结束后我一般会追问几个问题你觉得原来的代码哪里最差你改完之后别人读起来和原来有什么不同如果下次遇到同样的场景你会直接写成新版本还是先写旧版本这三个问题比任何测验都能反映孩子的真实理解水平。6.2 更长远的能力指标长期来看重构练习对孩子编程能力的影响会体现在几个可观察的指标上。第一新写的代码从一开始就更清爽不再需要事后大规模整理。第二读陌生代码的能力变强能很快找出主要结构和薄弱点。第三愿意回到旧代码里做修改而不是一遇到问题就推倒重写。第四对代码是写给人看的这句话有了自己的体会。这四个指标里前两个可以短期看到后两个需要更长时间才会显现。但只要孩子能保持小步练习它们会逐渐变成稳定的编程习惯。6.3 下一步从重构自己的代码到重构别人的代码如果孩子已经能稳定重构自己的代码下一步可以试着让他重构一段别人写的代码很多课程里也会设计给一段糟糕代码让同学们优化的环节。这一步的挑战在于孩子不熟悉原作者的思路必须先通过代码推断当时的想法才能动手改。这其实非常接近职业开发者的日常拿到的代码往往不是自己写的读代码的时间远多于写代码的时间。能在12岁就完成这个练习性价比非常高。做完之后最好再安排一次原作者和新读者的对话让原作者看重构后的版本说说能不能接受让孩子解释每个改动的理由。这一步会把重构练习从改代码升级成理解别人、表达自己的沟通练习收获远不止于代码本身。最后说一个我自己的判断。低龄重构的价值从来不在重构这个术语本身而在于它逼迫孩子完成一次完整的读代码、发现问题、动手修改、运行验证、讲给别人听的闭环。这个闭环对成年人来说稀松平常对一个十二岁的孩子来说却是编程学习中第一次真正地对自己写的代码负责。所以下次再看到12岁小学生重构代码这种话题别急着当成噱头。拿一段几十行的旧代码陪着孩子跑一遍、改一遍、讲一遍比什么都有用。
返回列表