阅读 4207

代码要写注释吗?写你就输了

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 HelloWorld杰少 即可关注。

如何看待程序员不写注释?

“几个月前,我写的代码只有我和上帝知道”

“现在,只有上帝知道了”

最近在知乎上看到了这个话题:怎样看待程序员不写注释? 看了下浏览量居然有 30+w 次,看来大家讨论的挺热闹,我浏览了大部分的回答,发现大家的观点可以归纳为以下几点:

  1. 不写注释就是害人害己,别人看不懂,过几天连自己也看不懂
  2. 好的代码就是最好的注释,我的代码可读性很好,没必要写注释
  3. 只要有完善的文档,代码本身就是注释
  4. 自己写代码时:“我自己写的代码还要写注释?” 看别人的代码时:“卧槽这人居然不写注释?”

对于程序员群体,有位知乎网友的总结非常到位:程序员最讨厌的四件事:1. 写注释 2. 别人不写注释 3. 写文档 4. 别人不写文档,不得不说我们程序员群体真是个可爱而又敢于自黑的群体。

image

说实话,我第一次看到这个话题的时候,我愣了一下,心想谁会提出这么沙雕的问题,在我看来写代码不写注释,那不就跟耍流氓一样嚒!在我第一天开始写代码的时候,我的老师就告诉我注释的重要性,就好比渴了要喝水,饿了要吃饭一样,这是编码的习惯。

image

我们写代码不仅仅是只给编译器看,还要给自己身边的同事看,假如有一天休假,你把工作交接给了你的同事社会王,你正吃着火锅唱着歌呢,社会王因为帮你解决 Bug 而又看不懂你的代码,给你来个夺命连环 Call,你还有什么心思度假。有人也会说:“我自己写的代码只要我自己看的懂就行”,可事实上不写注释,时间一久,等需要重新拾起来的时候你就会发现:“卧槽,这是啥?这为啥报错”。所以给代码添加合格的注释不仅能够让别人更快速的接手你的代码,也能更快的让拾起自己的代码。

image

迷惑行为

在正式的给大家讲解如何写出合格的注释之前,先给大家看下各种脑洞大开的代码注释:

斗图型

image

image

image

image

吐槽型

image

image

image

image

炫耀型

这是学生时期的谢尔盖·布林,后来 Google 的联合创始人之一,这位小伙子当时“低调”地把自己的待遇需求写在了注释里。翻译成中文大概意思就是“钱多事少大办公室,经常免费去好玩的地方旅行,好耶!

image

雷军 23 年前写的代码

image

/* * You may think you know what the following code does. * But you dont. Trust me. * Fiddle with it, and youll spend many a sleepless * night cursing the moment you thought youd be clever * enough to "optimize" the code below. * Now close this file and go play with something else. */
复制代码

中文:你可能相信你能看懂以下代码,但是其实绝对不可能,相信我。一旦你调试了,你绝对会后悔装聪明去尝试优化这段代码。最好的方式是关闭文件,去玩点儿你喜欢的东西吧

菜鸡型

// I am not sure if we need this, but too scared to delete. ... ...
复制代码

中文:个人不确认是不是需要,但是实在不敢删除

// I am not responsible of this code. // They made me write it, against my will.
复制代码

中文:个人不负责这块的质量,因为他们逼迫我违心的写了这段代码

//This code sucks, you know it and I know it. //Move on and call me an idiot later.
复制代码

中文:这段代码的确很挫,我知道你也知道,先不要骂我蠢,请先接着往下看.

看到上面这些灵魂极其有趣的程序员们各种脑洞大开,整起了各种类型的花活,我就只想问你们一下,你们的经理是不是不 review 你们的代码?

image

如何优雅的为程序写注释?

“好的代码不需要注释”不等于“没有注释就是好的代码”,好的代码不需要注释的前提是后面阅读你代码的人水平不能比你差太多,要写出那种新人都能看懂的代码,实在是太难了。

image

说到这里想必大家也知道了注释的重要性,注释可以帮助开发者在没有阅读代码的情况下快速了解该接口的功能和用法,如果注释写的好,你的代码也就更受人欢迎。

既然注释有这么多好处,那还不来学习一下一名合格的程序员该如何写注释。

以下注释遵循 C++ 和 Swift 规范, 注释选自开源项目:Kingfisher 和 Alamofire

利用好注释模板

注释模板为注释写作提供了极大的便利,我们常用的开发工具如 VS Code,Xcode 都对注释模板有很好的支持;例如 Xcode, 只要在需要注释的代码的上一行按下快捷键:opt + cmd + / 就可以添加注释模板。

示例如下:

image

注释风格

// 或者 ////* */ 都可以; 但 // 更 常用, 要在如何注释及注释风格上确保统一。

文件注释

文件注释描述了该文件的内容,每个文件注释都要包含以下内容:文件内容的简要描述,作者信息,日期,版权信息声明

文件注释的顺序应该如下:

  1. 文件内容的简要描述
  2. 作者信息
  3. 日期
  4. 版权信息声明,例如:Copyright 2008 Google Inc。 必要的话还可以加上许可证样板,例如:Apache 2.0, BSD, LGPL, GPL

示例如下:

//
//  Kingfisher.swift
//  Kingfisher
//
//  Created by Wei Wang on 16/9/14.
//
//  Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
复制代码

类注释

类注释应该要为读者提供使用该类的足够信息, 同时应当提醒读者在使用此类时要注意的事项。

示例如下:

import Foundation

/// `Session` creates and manages Alamofire's `Request` types during their lifetimes. It also provides common
/// functionality for all `Request`s, including queuing, interception, trust management, redirect handling, and response
/// cache handling.
open class Session {
	...
}
复制代码

函数注释

我们要在每个函数的声明处前加上注释, 描述该函数的功能和用途. 只有在函数的功能通俗易懂时才可以省略这些注释 (例如, 简单的取值和设值函数).。

示例如下:

/// Creates a `DataRequest` from a `URLRequest` created using the passed components and a `RequestInterceptor`.
    ///
    /// - Parameters:
    ///   - convertible:     `URLConvertible` value to be used as the `URLRequest`'s `URL`.
    ///   - method:          `HTTPMethod` for the `URLRequest`. `.get` by default.
    ///   - parameters:      `Parameters` (a.k.a. `[String: Any]`) value to be encoded into the `URLRequest`. `nil` by
    ///                      default.
    ///   - encoding:        `ParameterEncoding` to be used to encode the `parameters` value into the `URLRequest`.
    ///                      `URLEncoding.default` by default.
    ///   - headers:         `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
    ///   - interceptor:     `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
    ///   - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided
    ///                      parameters. `nil` by default.
    ///
    /// - Returns:       The created `DataRequest`.
    open func request(_ convertible: URLConvertible,
                      method: HTTPMethod = .get,
                      parameters: Parameters? = nil,
                      encoding: ParameterEncoding = URLEncoding.default,
                      headers: HTTPHeaders? = nil,
                      interceptor: RequestInterceptor? = nil,
                      requestModifier: RequestModifier? = nil) -> DataRequest {
        let convertible = RequestConvertible(url: convertible,
                                             method: method,
                                             parameters: parameters,
                                             encoding: encoding,
                                             headers: headers,
                                             requestModifier: requestModifier)

        return request(convertible, interceptor: interceptor)
    }
复制代码

变量注释

通常我们取变量名称的时候已经将其意义说明了。但是在逻辑复杂的情况下, 还是需要添加一些注释说明来做特别说明。

示例如下:

/// Underlying `URLSession` used to create `URLSessionTasks` for this instance, and for which this instance's
    /// `delegate` handles `URLSessionDelegate` callbacks.
    ///
    /// - Note: This instance should **NOT** be used to interact with the underlying `URLSessionTask`s. Doing so will
    ///         break internal Alamofire logic that tracks those tasks.
    ///
    public let session: URLSession
复制代码

TODO 注释

对那些临时的, 短期的解决方案, 使用 TODO 注释。

示例如下:

// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature
复制代码

弃用注释

通过注释(DEPRECATED comments)来标记接口已弃用,并要说明后续的替代方案。

示例如下:

/// Gets an image deserialized from provided data.
    ///
    /// - Parameters:
    ///   - data: The data from which an image should be deserialized.
    ///   - options: Options for deserialization.
    /// - Returns: An image deserialized or `nil` when no valid image
    ///            could be deserialized.
    /// - Note:
    /// This method is deprecated. Please implement the version with
    /// `KingfisherParsedOptionsInfo` as parameter instead.
    @available(*, deprecated,
    message: "Deprecated. Implement the method with same name but with `KingfisherParsedOptionsInfo` instead.")
    func image(with data: Data, options: KingfisherOptionsInfo?) -> KFCrossPlatformImage?
复制代码

最后

注释虽然写起来很痛苦, 但对保证代码可读性至关重要。 总而言之,学会写好代码注释,是每一个程序员的必备技术,也是一种良好的编程习惯,不仅仅是为了开发团队中的小伙伴,也是为了面对将来的自己。

参考资料:

  1. zh-google-styleguide.readthedocs.io/en/latest/g…
  2. bbs.huaweicloud.com/blogs/17619…
  3. github.com/onevcat/Kin…
  4. github.com/Alamofire/A…
  5. www.zhihu.com/question/27…

往期文章:

请你喝杯 ☕️ 点赞 + 关注哦~

  1. 阅读完记得给我点个赞哦,有👍 有动力

  2. 关注公众号--- HelloWorld杰少,第一时间推送新姿势

文章分类
代码人生
文章标签