📄 一句话概括
可重用性不是抽象口号:它等于【第二次使用时的成本】——实测内联版要用第二次只能复制,拆函数版直接调用;而单看改一次的成本,两边都是 1。
📝 本节和前面几节的关系
10.1 划清了范围,这一节讲为什么设计的重心变了:从【跑得快】变成【改得动】。
一、重心为什么变了
硬件便宜了、编译器强了——为省一点内存而写难懂的代码,收益越来越小。 软件活得越来越久:一套系统用十年,改二十次,写只写一次。 所以成本的重心从【写】移到【改】,设计的目标自然跟着移。
二、实测:改一次看不出差别
同一功能两种写法:内联版把求和、加税、保底、取整全压在主流程里;拆函数版各拆一个函数。 改一次税率:两边都是 1 个改动点。 改一次取整规则:也都是 1 个。 所以只改一次的话,拆函数没占便宜——它还多了 4 个函数。
三、差别出现在第二次使用
要把取整用到别处:内联版只能复制那 1 行,拆函数版直接调 1 次。 要单测取整:内联版没法单独测(只能整条流程测),拆函数版能测 5 段。 这就是可重用性的量化定义:第二次使用时的成本。
四、代价也要说清
拆得太多会失去整体感:一个功能跳 8 个函数,读起来像拼图。 性能上确实多一层调用,但现代语言里这点开销通常可忽略。 所以判据不是【拆得越细越好】,而是【这块会不会被用第二次】。
五、动手 Demo:相比运行效率,现在更重视可维护性和可重用性
对比内联版与拆函数版在几种情况下的成本。 注意【改一次】这一行两边相同,差别在【用第二次】。
# 内联版:四件事压在主流程里
r = a * 1.1 + b * 1.1 + c * 1.1
r = (r + 0.5) * 1.03
r = r if r > 0 else 0
r = int(r * 100) / 100
# 拆函数版:四个小函数各管一件事
r = total(a, b, c)
r = with_tax(r)
r = clamp(r)
r = round2(r)
实跑输出:
--------------------------------------------------------------------
相比运行效率,现在更重视可维护性和可重用性
--------------------------------------------------------------------
同一功能两种写法:**一种为速度写了三大段内联,一种拆成小函数。**实测改动成本:
对比项 内联版 拆函数版
--------------------------------------------------
函数个数 1 5
改一次税率的改动点 1 1
改一次取整规则的改动点 1 1
把取整用到别处 复制 1 行 调 1 次
单测能测几段 1 段 5 段
内联版行数 4 行
拆函数版行数 4 行
内联版可单独测试的部分 1 段
拆函数版可单独测试的部分 5 段
**注意【改一次的改动点】两边都是 1**——单看这一点,看不出差别。
**差别在【用第二次的时候】**:内联版只能复制,拆函数版直接调用。
**所以可重用性不是抽象口号,它等于【第二次使用时的成本】。**
实测对照:改一次税率或取整规则,两边都只有 1 个改动点。 差别在【用第二次】:内联版只能复制,拆函数版直接调用;单测从 1 段变 5 段。 所以可重用性=第二次使用时的成本,不是一句口号。
30 秒实验
- 找出你代码里被复制三遍以上的那段逻辑,算算第三次复制的成本。
- 找出一个拆得过碎的功能,数数读一遍要跳几个函数。
常见误区
- 以为可维护性只是好听的词。它可以直接量:第二次使用的成本。
- 以为拆得越细越好。读一遍要跳八个函数,也是负担。
自测三问(点开对答案)
1. 为什重心从效率转向可维护? —— 硬件便宜了、编译器强了,而软件活得久、改的次数远多于写的次数。 2. 实测两边差别在哪? —— 改一次都是 1 个改动点;差别在第二次使用时,内联只能复制、拆函数可直接调用。 3. 可重用性怎么量化? —— 等于第二次使用时的成本;同时单测可分的段数也从 1 段变成 5 段。- ☐ 10.3 设计目标之一:去除重复
相关笔记
- 设计的目标范围很广 — 设计都管什么
- 三大要素之三:去除类的重复定义的继承 — 去重的主力手段
- 设计目标之一:去除重复 — 第一个目标展开
- 🧩 面向对象是怎样工作的 · 索引