《A Philosophy of Software Design》心得 3 — 写程式时该写注解 (comments) 吗?如果要的话该怎么写?

2023年1月15日

💎 加入 E+ 成長計畫 如果你喜歡我們的內容,歡迎加入 E+,獲得更多深入的軟體前後端內容

每当提到在程式中写注解(comments),你大概会在网路上看到两派人马,有人觉得应该要写注解;又有另一群人觉得代码应该要写得够清楚,如果有注解就代表写不够清楚,应该要重构而不是加注解。当然除了这极端的两派人马外,多数人都是在中间,部分的代码写注解,但不会全部都写。

关于写注解这件事,在 《A Philosophy of Software Design》 书当中也有谈及。 John Ousterhout 教授的观点是,如果注解写得好,将有效改善整体的系统设计。假如你是反注解派的人,或许可以一起来读读他为什么这么认为。

程式本身写得够清楚,就不用注解吗?

「程式本身写得够清楚,就不用注解」是很多人认为不该写注解的第一大理由。然而程式本身或许可以清楚表达该代码做的事,但不能解释「为什么」要这样写,也不能解释在什么情境可能比较适合某个方法。对于这些相对后设 (meta) 的内容,注解就是很好的帮手。

更进一步说,好的软件设计,应该要把复杂度藏起来,要藏起复杂度,我们需要抽象化,让其他使用该段代码的开发者,可以不用去管背后的细节。因此,如果一个开发者,还要去读程式的细节才能懂如何用该函式或方法,那就失去抽象化的意义。写注解可以让其他开发者,不用去读程式里头的细节也知道如何用该函式或方法,这才能达到抽象化的意义。因此不论程式本身写的清楚与否,好的抽象化搭配好的注解,都是对整个程式库的维护有帮助的。

别说你没时间写注解

在开发时,很多开发者会认为写注解的优先顺序比较低,因此会想把时间花在写新功能,而不是为已经开发好的功能写注解。然而在软件开发中,永远有写不完的功能,如过说因为要开发新功能而没时间写注解,就永远不会有注解。从长远的角度来看,这样的代价是程式库的可维护性会降低。如果从效益与成本的角度来看,写注解的时间,会远比其他开发者花在弄懂没注解的程式所花的时间要少很多。

不要找其他不写注解的理由

很多开发者也会找其他理由不写注解。例如认为注解如果没随着程式更新,反而会误导人;或者是因为过去读过别人写的注解觉得没用,就认为注解没有用。这些都是外部因素,无法证成注解本身没有效益。此外这些都是可以透过好的流程而解决的,所以作者认为不要找这些理由不写注解,而是要去打造更好的流程来改善这些问题

写注解的好处

上面谈了这么多,大概可以看出 John Ousterhout 教授有强烈的观点认为要写注解。不过回过头来说,写注解有什么好处呢? 他认为最大的好处在于注解可以捕捉到代码没办法传达的资讯,这能够让未来要维护这段代码的开发者,能够更快速地上手。特别对于团队中加入的新成员,代码中的注解可以大幅降低读程式的认知负担 (cognitive load),以及减少未知。

如果没有记录下这些脉络与原因,未来的开发者就不会知道为什么当初是这样写,这变得只能猜测原作者的意图。这不仅更耗时,也可能会因为理解错原本的意图,导致在维护该段代码时出错。很多时候,未来要维护代码的是自己,许多人在写完某段代码几个月后,可能忘了自己当初为什么这样写,所以写注解不仅是帮助其他开发者,也很可能是在帮助未来的自己。

注解该写什么?

虽说 John Ousterhout 教授的观点是认为写注解对软件设计有帮助,但他也同意不是什么都该写成注解。他认为只有在代码中不显而易见的才该写注解 (comments should describe things that aren’t obvious from the code)

在软件设计中最重要的抽象化,即是提供一个更简易的思考方式,让开发者可以不用深入过于细节的部分。即使开发者可以透过细读代码来了解其如何运作,但这样做太花时间。 John Ousterhout 教授的观点认为开发者应该要不读代码内容,即可理解模组提供的抽象化,而注解是能够协助做到这样的方法。

不要重复代码本身

前面提到,注解应该要讲「为什么」这样写,而不是讲代码在做什么。毕竟代码本身就代表代码在做的事,如果注解在写一次,会是多此一举。下面是书中提到的负面教材,基本上注解就是把代码做的事情。

ptr_copy = get_copy(obj)              # Get pointer copy
if is_unlocked(ptr_copy):             # Is obj free?
  return obj                          # return current obj
if is_copy(ptr_copy):                 # Already a copy?
  return obj                          # return obj
thread_id = get_thread_id(ptr_copy)
if thread_id == ctx.thread_id:        # Locked by current ctx
  return ptr_copy                     # Return copy

要怎么判断你写的这段注解是不是跟代码本身一样? 书中提到,在你写完注解后可以问问自己「如果某个之前没读过这段代码的人,能不能看着这段注解写出代码?」如果可以的话,就代表注解只是在描述代码,而不是在解释为什么这样写该段代码。因此,在写完注解后,可以再比对一下代码与注解,如果你的注解像是下面那长梗图一样(代码本身是 Stop Sign,然后注解写着「这是一个 Stop Sign」) ,这种状况下注解就真的没有额外价值,不推荐写。

重复代码的注解就像这个立牌一样This is a stop sign -> Stop Sign
重复代码的注解就像这个立牌一样This is a stop sign -> Stop Sign
圖片來源:《A Philosophy of Software Design》

从读者的角度出发

最后 John Ousterhout 教授提到,写注解要从代码读者的角度出发,要思考有哪些关键资讯是读者不知道的。特别是在代码审查(code review) 时,如果你写的某段代码让别人说很难懂,或者某个资讯让别人说并不显而易见,这时候不要去跟对方争,而是去想为什么对方会有困惑,以及你可以如何靠注解让代码更好被理解。

(文末备注:这个章节本身有很多范例,但为了避免笔记变太长,就仅有放其中一个。有兴趣透过案例更具体了解作者的观点的话,推荐直接买这本书来读)。


《A Philosophy of Software Design》心得系列文

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們