🧩 第十章 面向对象设计:拟人化和职责分配
📄 一句话概括

可重用性不是抽象口号:它等于【第二次使用时的成本】——实测内联版要用第二次只能复制,拆函数版直接调用;而单看改一次的成本,两边都是 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. 以为可维护性只是好听的词。它可以直接量:第二次使用的成本。
  2. 以为拆得越细越好。读一遍要跳八个函数,也是负担。
自测三问(点开对答案) 1. 为什重心从效率转向可维护? —— 硬件便宜了、编译器强了,而软件活得久、改的次数远多于写的次数。 2. 实测两边差别在哪? —— 改一次都是 1 个改动点;差别在第二次使用时,内联只能复制、拆函数可直接调用。 3. 可重用性怎么量化? —— 等于第二次使用时的成本;同时单测可分的段数也从 1 段变成 5 段。

相关笔记