编程语言基础
软件工程
如何去开发一个真正能用于实践的程序。
通过本节课,你将理解:
#goal前面各课里,程序多半装在一个文件里:写完、跑通、读懂即可。真实世界里的软件往往更大——多人接力、长期修改、出错代价更高。软件工程要回答的不是「还能不能再写一行」,而是:怎样把可执行的思想组织成可分工、可检验、可演进的形态。
本节课介绍几件贯穿现代软件的骨架:模块化与分层(含常见的 B/S 结构)、单元测试、编程框架、版本管理。目标是建立概念地图,而不是立刻搭起一整套工程。下一课讲氛围编程时,你会看见这些骨架如何被工具加速——也如何被草稿悄悄绕开。
从一份脚本到一份工程
#hook一份课堂作业可以只有一个 .py。一款要上线的应用,却通常拆成许多文件、约定许多接口、留下许多「怎样才算做对」的检查,并且每一次改动都要能回看、能回退。

清单之所以能堆到身高,不是因为有人喜欢纸张,而是因为思想必须被拆开、编号、复核。软件工程继承的正是这种纪律:把「能跑」升级为「别人接得住、机器查得了、历史说得清」。
模块化:把能力拆成可替换的单元
#explore-modular模块化的想法你在第 10 课已经见过:共用的能力放进模块,当前页面只处理当前问题。工程尺度上,它还意味着:
- 按职责拆文件:画图的放一处,计算的放一处,读配置的放一处。
- 用接口交接:别人只需要知道「调用什么、传入什么、返回什么」,不必打开你文件的每一行。
- 改一处,别处尽量不碎:修正
shapes.py里的正方形,不应逼你重抄三份粘贴出来的副本。
下面是一个极小的示意——不是完整项目,只说明「拆」与「接」:
# shapes.py(示意:几何)
def square_side_count():
return 4
# main.py(示意:使用方)
import shapes
print('正方形边数', shapes.square_side_count())抄函数进许多文件,和 import 不是一回事。模块化要消灭的,正是「同一规则的许多份失同步副本」。
分层与 B/S:谁负责看见,谁负责计算
#explore-layers模块化解决「拆成哪些盒子」。分层解决「盒子之间谁可以叫谁」——通常只允许上层依赖下层,避免所有人互相牵扯。
一个常见的三层说法(名字因领域而异,结构相似):
- 表现层:人看见、点选、输入的地方(网页、窗口、海龟画布旁的按钮)。
- 业务层:规则与流程(如何计分、如何生成下一帧、何种输入合法)。
- 数据层:保存与读取(文件、数据库、远程接口)。
B/S 结构(Browser / Server,浏览器—服务器)是分层在网络上的一种落地:浏览器负责展示与交互,服务器负责业务与数据。你打开的网页是 B 端;算分、存档、鉴权往往在 S 端。同校同学的成绩不应只存在某台浏览器的临时内存里——那是分层要保护的边界。
对照本课的在线环境:编辑与画布在浏览器一侧;解释器也可能在远端执行。即便只有你一人使用,「谁看见结果、谁保存状态」 仍然是工程问题。大系统用防火墙与接口把边界写硬;小程序用文件与函数把边界写清。
分层不是越多越好。多一层,就多一处交接与命名。问题变大时再分层;问题仍小却先搭三层空架子,属于写法带来的负担。
单元测试:让「做对了」可重复执行
#explore-test人眼看一次输出,容易漏。单元测试把「这一小块在这些输入下应得到什么」写成可再跑的程序。改完代码后整批重跑,哪一条红了,就知道哪一块被碰坏。
Python 里可以用最朴素的 assert,也可以用 unittest / pytest 等工具。下面用 assert 说明思想——测的是一个小函数,不是整幅画布:
def exterior_angle(sides):
return 360 / sides
assert exterior_angle(4) == 90
assert exterior_angle(6) == 60
# exterior_angle(0) 应如何失败?测试也会逼你想清楚边界。测试写的是契约:合法输入的结果、非法输入是否拒绝。它不替代阅读,但把验收从「我记得昨天好像对」变成「现在再跑一遍仍对」。
框架:先有骨架,再填血肉
#explore-framework编程框架是别人写好的结构与约定:你按它的插槽填入自己的逻辑,而不是从空白文件发明全部流程。Web 框架规定请求如何进来、响应如何出去;游戏框架规定每帧如何更新;GUI 框架规定按钮如何挂上事件。
框架与模块的差别可以这样记:
- 模块像工具箱:你决定何时拿起哪把扳手。
- 框架像装配线:主流程由它驱动,你的代码被「叫进去」干活(所谓控制反转)。
学框架要付的学费是:先读懂它的地图(目录、入口、生命周期),再写自己的三行。跳过地图直接粘贴示例,短期能跑,长期迷路。氛围编程尤其容易生成「某个流行框架的完整脚手架」——即便你的目标只是课堂海龟动画。
版本管理:给思想做可回退的快照
#explore-vcs版本管理(常见工具是 Git)记录每一次有意义的改动:谁、何时、改了什么、为何改。它让你能:
- 回到「昨天还能画对五角星」的那一版;
- 并行尝试两个方案再合并;
- 看清一行可疑代码是哪次提交引进的。
一次提交应当是一个说得清的意图,而不是「下午随便保存」。提交说明写的是思想,不是心情:「修好左枝未后退」比「update」有用。
课堂练习未必强制使用 Git,但要建立这个概念:没有历史的工程,只能靠人脑记忆;人脑不是可靠的备份介质。
本节练习:平均雨量的一张草图
#practice不必写完整程序。在纸上为「由一组雨量求出平均值」画一张最小草图:计算放在一个模块里,入口只负责把读数交进去并取出结果。契约用这组数据:读数为 1、2、3 时,平均值应为 2。边界也写死:读数为空列表 [] 时,不计算平均值,而是说明「没有读数」。一份来源、一条可再跑的验收,比把同一公式抄进多处更接近工程。
本节要点
#review- 软件工程把「能跑」扩展为可分工、可检验、可演进。
- 模块化按职责拆分并用接口交接;分层规定依赖方向,B/S 是网络上的常见分层。
- 单元测试把验收写成可重复执行的契约。
- 框架提供主流程骨架;版本管理给改动留下可回退的历史。
单选题
模块化在保护什么
把同一函数复制进三个文件,与写成一个模块再 import,主要差别是?
单选题
B/S 在说什么
关于 B/S 结构,哪一项更准确?
单选题
单元测试测什么
单元测试更接近哪一件事?
多选题
工程骨架
下列哪些属于本节介绍的软件工程手段?可以多选。
可多选
未登录时不会保存学习进度