Loading

<<代码整洁之道>> 读书笔记(1-5)

整洁代码

  1. 人工智能永远不能完全取代程序员, 因为客户的需求总是模糊的, 程序员不只是写代码, 也会去讨论/设计需求和架构
  2. 糟糕的代码会杀死项目, 通常会在项目中后期体现出来, 此时项目的生产力快速下降, 影响正常迭代和问题修复
  3. 对一个成熟的项目进行重新设计和编写, 往往会分散人力, 同时新版的项目要想替换老项目也会耗时很久, 可能因为中间的人力变更导致烂尾
  4. 程序员要积极的拥抱项目发展中的各种需求变更, 不要总是抱怨太多杂事和需求变更导致开发时间紧迫, 事实上应该和PM沟通, 说明时间的用处和压缩时间的坏处, PM也希望可以给多的时间来产出优秀的代码.
  5. 整洁的代码是能让其他读者觉得, 这个代码是编写者用心写的, 几乎没有可以改进的地方, 其他人没有办法让这段代码变得更好了.
  6. 在编写代码的时间里, 实际上我们需要一直读之前的代码. 因此, 提高代码可读性是非常重要的.
  7. 时刻的记住保持代码的整洁, 从细微处做起.

有意义的命名(变量名/函数名/类名/文件名/文件夹名...)

  1. 选一个好名字需要花时间思考, 但是这是绝对值得的, 可以大大提高可读性
  2. 名字应该直观的体现出它存在的意义(为什么存在/做了什么事/怎么使用)
  3. 任何时候, 如果名字需要添加注释才能明白其意义, 那么就不是有意义的命名
  4. 名字要避免误导, 尤其是将变量名缩写(比如 userName->un)
  5. list一般代表列表, 不要给不是列表的变量名添加list
  6. 多个命名如果相似度很高也会让人不小心看错
  7. 小写字母I和大写字母O很可能会被看错成小写字母i和数字0
  8. 命名只以其中的数字做差异区分, 是错误的做法(比如 a1, a2, a3, ...)
  9. 相似意思的命名也是错误的(比如 userInfo 和 userData 都是用户数据的意思)
  10. 命名中不要出现废话, 给命名加上类型一定要慎重(比如 nameString, name 难道还能不是 string 类型吗?)
  11. 命名要保证可读性, 不要违背命名的正常使用(比如 userName 比 nameForUser 更加符合常识)
  12. 命名要易于搜索, 方便在修改名字时搜索到(比如 err相比于e, e可能是某个名字的其中一个字母, 在搜索时可能有很多结果, 易混淆)
  13. 短命名只适用于短方法中的本地变量, 名称的长短应该与其作用域的大小正相关
  14. 对于强类型语言, 不需要在命名中体现出来这个命名的值的类型是什么(强类型语言的phone能代表phoneNumber)
  15. 不必给成员的变量添加 m_ 前缀, 应该让命名更有意义
  16. 不必给接口命名统一加上前缀 I
  17. 减少使用短命名, 不要让读者猜测和适应你的命名规则(比如把 url 命名为 r)
  18. 类名和对象名应该是名词或者名词短语, 而不是动词, 同时名词也应该有具体的意义(比如 Info/Data/Set)
  19. 方法名应该是动词或者动词短语
  20. 命名不要参杂一些搞笑的/耍宝的词
  21. 给每一个抽象的概念起一个独有的名字, 并且在命名中体现, 保持每个命名都是独一无二的, 这样才能快速的找到对应的命名(比如 每个抽象都有get, 我们很难知道这个get是对应哪个)
  22. 不要使用双关语, 即一个命名在多个抽象中代表不同意思, 不要为了整齐而忽视了代码整洁的目的, 提高可读性(比如 add在a抽象是添加的意思, 在b抽象是追加, 那么b应该是append而不是add, 不能因为保持一致强行命名)
  23. 读代码的人始终是程序员, 因此命名应该紧贴程序员的涉及领域, 而专有领域的专用词汇, 只有普通命名真的无法定义这个意义, 才考虑使用专用词
  24. 如果命名无法准确的自我说明, 就需要添加前缀了, 可以给命名添加对应的语境, 来更好的说明命名的意义(比如 firstName lastName city, 如果都是地址中的某一个部分, 可以统一加上 address 的前缀), 当然更好的方法是将其归为一个类的多个属性, 此时就不需要前缀了(例如 将address 变成一个类, firstName lastName city 都是其中的属性, 那么就更加的易懂了)
  25. 不要添加无用的语境, 命名如果有大量的统一前缀, 说明代码结构出现了问题

函数

  1. 函数一定要短, 一个函数控制在20行内是最好的
  2. if/else/while 等重复或者分支逻辑块中的代码最好只有一行, 这一行是调用另一个处理函数, 同时这个函数名需要易懂, 具有很好的说明性.
  3. 函数的缩进层级不应该多于一层或两层, 这样更有利于理解和沟通.
  4. 一个函数应该做一件事, 做好这件事, 并且只做这件事. 如果你觉得一个函数内部还能再拆出来一个函数, 那么就说明这个函数不止做一件事.
  5. 函数内部的应该是从上到下, 逻辑是相接的, 不是跳跃的.
  6. 函数内的 switch/if-else 天生是做分支处理, 因此他没有办法只做一件事, 但是可以使用抽象工厂模式将分支处理包裹在在创建对象逻辑中, 避免后续由外部调用者更改, 同时尽量的减少抽象层级, 并且分支永远不重复(指的是 将所有分支作为多态实现对应接口, 有一个创建函数, 负责分支处理, 并创建不同的类型给到调用者, 调用者只需要调用他们的接口即可)
  7. 函数越小, 功能越集中, 函数名就越好起
  8. 长并且具有描述性的名称比短但是令人费解的名称好
  9. 长并且具有描述性的名称比长并且具有描述性的注释要好
  10. 最理想的函数参数个数是0, 即不需要参数 其实是1, 再是2, 最好不要3或者超过3
  11. 函数参数越多, 测试写起来越困难, 因为要测试多个case
  12. 参数名字要体现出来参数的意义和作用, 要和函数名有上下文关联性
  13. 不要使用标识参数(bool), 因为这代表着这个函数内有多种处理流程, 违背了函数只做一件事的原则
  14. 如果不是非常明显需要两个或三个参数的函数(例如笛卡尔乘积就需要2个毫不相干的参数), 可以考虑将两个参数合并为一个类, 调用者传入这个对象, 开发者拿取对象内的属性进行处理.
  15. 如果函数需要多个参数, 考虑是否能将有关联性的参数合并为一个类参数, 来减少参数数量
  16. 如果函数有多个参数, 这些参数是同类型和同意义的, 可以将其设置为可变参数(例如 format 语句)
  17. 可以将函数名设置为动词, 将参数设置为对应的名词.(例如 DeleteFile(file) )
  18. 如果函数逻辑具有破坏性, 应该在函数名中有所体现, 此时就算违背只做一件事的规则也必须这样处理(例如 checkUserPassword 假如会造成 user 的session初始化, 那么必须将函数名修改为 checkUserPasswordAndInitSession , 这样避免调用者用错函数而导致逻辑BUG)
  19. 输出参数可以通过直接修改对象的属性来取消使用(只支持面向对象语言)
  20. 函数要么做什么事, 要么回答什么事, 二者不可得兼(例如 setAndCheckIsExists(attribute, value) bool 目的是设置对象的 attribute 属性的值为 value, 同时如果没有这个属性, 就返回 false, 那么这个函数让人调用时无法写出优雅的代码, 他像 if(setAndCheckIsExists(”age”, “18”)) , 最好将检查和塞入分开为两个函数)
  21. 如果函数的作用是做什么事, 那么在有错误时, 直接抛出异常比返回错误码更好, 这样可以避免调用者嵌套进行错误判断, 调用者直接捕捉异常即可(不适用golang)
  22. 将错误捕捉和处理的逻辑抽离出来, 另外形成函数被调用, 不要和正常的逻辑合在一起, 前提是错误处理是相对独立的, 不影响正常的流程(例如 注销程序时需要删除文件, 如果文件删除失败也无所谓, 那么将删除和删除失败的错误捕捉单独抽出函数)
  23. 错误处理只进行错误处理, 不进行其他的操作
  24. 将错误码进行返回, 在错误码更新时会导致调用者代码有可能更改, 而通过直接抛出异常, 不需要重新处理
  25. 将重复的逻辑封装成函数, 避免80%代码都一样的函数出现多个
  26. 高级的程序员将系统当作故事来讲, 而不是当作程序来写, 阅读它的代码就像在读一个故事(比如 用户登录, 先从数据库中获取账密, 在与用户提交信息进行对比, 再查看用户状态是否异常 这些流程可以通过函数名和方法名来直观的让人理解和读取)

注释

  1. 注释是用来弥补代码的可读性的, 好的代码其实并不需要注释
  2. 好的注释可以提高代码可读性, 坏的注释更可以破坏代码可读性
  3. 程序员往往不会维护注释, 注释存在的时间越久, 往往跟现有的逻辑偏差越大(很多人只修改代码逻辑而不去更新注释)
  4. 代码是唯一可信的
  5. 注释并不能优化糟糕的代码, 不要依赖注释来提高代码可读性, 而是着手于更新代码
  6. 注释可以写法法律信息,但是要注意不要长篇大论,最好是一个指向详细信息的链接(前提是公司要求必须携带法律信息)
  7. 如果变量名或者逻辑能够让人直接看懂, 那么注释是多此一举的
  8. 注释内容可以是: 我为什么这样做, 目的是来对某些复杂或者迫不得已的逻辑进行解释和辩护
  9. 注释内容可以是说明变量的意义, 但是记住优先看看变量名是否可以优化
  10. 注释内容可以是警告, 告诉读者不要进行某些危险操作, 前提是你觉得其他人很容易进行误操作
  11. 注释内容可以是TODO, 但是注意, 针对已经完成的TODO务必将注释删除
  12. 注释一定是需要有意义的
  13. 注释并不是一定需要的, 也不能是一个规定(比如规定只要是函数必须有注释)
  14. 如果逻辑变化, 一定要删除老的注释编写新的注释或者不写, 不能像更新文档一样将多个版本变化堆叠到一起
  15. 注释的内容不要是XXX在某个时间编写了这个代码或文件,或者其他的归属或署名, git比注释更加可靠, 并且事实上代码总是会不停迭代
  16. 直接把代码变成注释是非常low的做法, 没有任何用处, 应当被清理掉
  17. 避免在注释里掺杂HTML, 可读性很差
  18. 注释只与当前最近的代码关联, 不要离得很远
  19. 注释要简单和干练
  20. 注释目的是解释代码, 注释不能让人无法看懂
  21. 函数头注释不一定需要, 给函数起一个好名字更重要

格式

  1. 团队的代码风格和格式要求应该保持一致
  2. 善用代码格式化工具(比如vscode的go插件)
  3. 代码文件的行数越短, 越利于理解
  4. 代码文件名应当简单且一目了然, 并且目录的层级可以清楚的表示逻辑的层级
  5. 代码中的段之间应该有空行进行分隔
  6. 紧密相关的代码之间不应该有空行
  7. 变量的声明应该尽可能地靠近他的使用位置
  8. 函数内部的本地变量应该在函数的顶部出现
  9. 循环内部的变量在循环内声明
  10. 如果函数有调用关系, 被调用函数应该紧紧贴在调用函数上方
  11. 把概念相关的函数放在一起, 它们之间的联系越紧密, 距离也应该越短
  12. 一行代码的长度不要很长, 最好是100字符以内
  13. 赋值操作符左右加空格来进行强调
  14. 运算符加上空格体现出运算顺序
  15. 水平对齐可以提高整洁度
  16. 缩进要与逻辑的层级对应
  17. 团队内的代码风格和规则应该保持一致
posted @ 2023-07-11 19:31  ChnMig  阅读(76)  评论(0编辑  收藏  举报