整洁代码
- 人工智能永远不能完全取代程序员, 因为客户的需求总是模糊的, 程序员不只是写代码, 也会去讨论/设计需求和架构
- 糟糕的代码会杀死项目, 通常会在项目中后期体现出来, 此时项目的生产力快速下降, 影响正常迭代和问题修复
- 对一个成熟的项目进行重新设计和编写, 往往会分散人力, 同时新版的项目要想替换老项目也会耗时很久, 可能因为中间的人力变更导致烂尾
- 程序员要积极的拥抱项目发展中的各种需求变更, 不要总是抱怨太多杂事和需求变更导致开发时间紧迫, 事实上应该和PM沟通, 说明时间的用处和压缩时间的坏处, PM也希望可以给多的时间来产出优秀的代码.
- 整洁的代码是能让其他读者觉得, 这个代码是编写者用心写的, 几乎没有可以改进的地方, 其他人没有办法让这段代码变得更好了.
- 在编写代码的时间里, 实际上我们需要一直读之前的代码. 因此, 提高代码可读性是非常重要的.
- 时刻的记住保持代码的整洁, 从细微处做起.
有意义的命名(变量名/函数名/类名/文件名/文件夹名...)
- 选一个好名字需要花时间思考, 但是这是绝对值得的, 可以大大提高可读性
- 名字应该直观的体现出它存在的意义(为什么存在/做了什么事/怎么使用)
- 任何时候, 如果名字需要添加注释才能明白其意义, 那么就不是有意义的命名
- 名字要避免误导, 尤其是将变量名缩写(比如 userName->un)
- list一般代表列表, 不要给不是列表的变量名添加list
- 多个命名如果相似度很高也会让人不小心看错
- 小写字母I和大写字母O很可能会被看错成小写字母i和数字0
- 命名只以其中的数字做差异区分, 是错误的做法(比如 a1, a2, a3, ...)
- 相似意思的命名也是错误的(比如 userInfo 和 userData 都是用户数据的意思)
- 命名中不要出现废话, 给命名加上类型一定要慎重(比如 nameString, name 难道还能不是 string 类型吗?)
- 命名要保证可读性, 不要违背命名的正常使用(比如 userName 比 nameForUser 更加符合常识)
- 命名要易于搜索, 方便在修改名字时搜索到(比如 err相比于e, e可能是某个名字的其中一个字母, 在搜索时可能有很多结果, 易混淆)
- 短命名只适用于短方法中的本地变量, 名称的长短应该与其作用域的大小正相关
- 对于强类型语言, 不需要在命名中体现出来这个命名的值的类型是什么(强类型语言的phone能代表phoneNumber)
- 不必给成员的变量添加 m_ 前缀, 应该让命名更有意义
- 不必给接口命名统一加上前缀 I
- 减少使用短命名, 不要让读者猜测和适应你的命名规则(比如把 url 命名为 r)
- 类名和对象名应该是名词或者名词短语, 而不是动词, 同时名词也应该有具体的意义(比如 Info/Data/Set)
- 方法名应该是动词或者动词短语
- 命名不要参杂一些搞笑的/耍宝的词
- 给每一个抽象的概念起一个独有的名字, 并且在命名中体现, 保持每个命名都是独一无二的, 这样才能快速的找到对应的命名(比如 每个抽象都有get, 我们很难知道这个get是对应哪个)
- 不要使用双关语, 即一个命名在多个抽象中代表不同意思, 不要为了整齐而忽视了代码整洁的目的, 提高可读性(比如 add在a抽象是添加的意思, 在b抽象是追加, 那么b应该是append而不是add, 不能因为保持一致强行命名)
- 读代码的人始终是程序员, 因此命名应该紧贴程序员的涉及领域, 而专有领域的专用词汇, 只有普通命名真的无法定义这个意义, 才考虑使用专用词
- 如果命名无法准确的自我说明, 就需要添加前缀了, 可以给命名添加对应的语境, 来更好的说明命名的意义(比如 firstName lastName city, 如果都是地址中的某一个部分, 可以统一加上 address 的前缀), 当然更好的方法是将其归为一个类的多个属性, 此时就不需要前缀了(例如 将address 变成一个类, firstName lastName city 都是其中的属性, 那么就更加的易懂了)
- 不要添加无用的语境, 命名如果有大量的统一前缀, 说明代码结构出现了问题
函数
- 函数一定要短, 一个函数控制在20行内是最好的
- if/else/while 等重复或者分支逻辑块中的代码最好只有一行, 这一行是调用另一个处理函数, 同时这个函数名需要易懂, 具有很好的说明性.
- 函数的缩进层级不应该多于一层或两层, 这样更有利于理解和沟通.
- 一个函数应该做一件事, 做好这件事, 并且只做这件事. 如果你觉得一个函数内部还能再拆出来一个函数, 那么就说明这个函数不止做一件事.
- 函数内部的应该是从上到下, 逻辑是相接的, 不是跳跃的.
- 函数内的 switch/if-else 天生是做分支处理, 因此他没有办法只做一件事, 但是可以使用抽象工厂模式将分支处理包裹在在创建对象逻辑中, 避免后续由外部调用者更改, 同时尽量的减少抽象层级, 并且分支永远不重复(指的是 将所有分支作为多态实现对应接口, 有一个创建函数, 负责分支处理, 并创建不同的类型给到调用者, 调用者只需要调用他们的接口即可)
- 函数越小, 功能越集中, 函数名就越好起
- 长并且具有描述性的名称比短但是令人费解的名称好
- 长并且具有描述性的名称比长并且具有描述性的注释要好
- 最理想的函数参数个数是0, 即不需要参数 其实是1, 再是2, 最好不要3或者超过3
- 函数参数越多, 测试写起来越困难, 因为要测试多个case
- 参数名字要体现出来参数的意义和作用, 要和函数名有上下文关联性
- 不要使用标识参数(bool), 因为这代表着这个函数内有多种处理流程, 违背了函数只做一件事的原则
- 如果不是非常明显需要两个或三个参数的函数(例如笛卡尔乘积就需要2个毫不相干的参数), 可以考虑将两个参数合并为一个类, 调用者传入这个对象, 开发者拿取对象内的属性进行处理.
- 如果函数需要多个参数, 考虑是否能将有关联性的参数合并为一个类参数, 来减少参数数量
- 如果函数有多个参数, 这些参数是同类型和同意义的, 可以将其设置为可变参数(例如 format 语句)
- 可以将函数名设置为动词, 将参数设置为对应的名词.(例如
DeleteFile(file)
)
- 如果函数逻辑具有破坏性, 应该在函数名中有所体现, 此时就算违背只做一件事的规则也必须这样处理(例如
checkUserPassword
假如会造成 user 的session初始化, 那么必须将函数名修改为 checkUserPasswordAndInitSession
, 这样避免调用者用错函数而导致逻辑BUG)
- 输出参数可以通过直接修改对象的属性来取消使用(只支持面向对象语言)
- 函数要么做什么事, 要么回答什么事, 二者不可得兼(例如
setAndCheckIsExists(attribute, value)
bool 目的是设置对象的 attribute 属性的值为 value, 同时如果没有这个属性, 就返回 false, 那么这个函数让人调用时无法写出优雅的代码, 他像 if(setAndCheckIsExists(”age”, “18”))
, 最好将检查和塞入分开为两个函数)
- 如果函数的作用是做什么事, 那么在有错误时, 直接抛出异常比返回错误码更好, 这样可以避免调用者嵌套进行错误判断, 调用者直接捕捉异常即可(不适用golang)
- 将错误捕捉和处理的逻辑抽离出来, 另外形成函数被调用, 不要和正常的逻辑合在一起, 前提是错误处理是相对独立的, 不影响正常的流程(例如 注销程序时需要删除文件, 如果文件删除失败也无所谓, 那么将删除和删除失败的错误捕捉单独抽出函数)
- 错误处理只进行错误处理, 不进行其他的操作
- 将错误码进行返回, 在错误码更新时会导致调用者代码有可能更改, 而通过直接抛出异常, 不需要重新处理
- 将重复的逻辑封装成函数, 避免80%代码都一样的函数出现多个
- 高级的程序员将系统当作故事来讲, 而不是当作程序来写, 阅读它的代码就像在读一个故事(比如 用户登录, 先从数据库中获取账密, 在与用户提交信息进行对比, 再查看用户状态是否异常 这些流程可以通过函数名和方法名来直观的让人理解和读取)
注释
- 注释是用来弥补代码的可读性的, 好的代码其实并不需要注释
- 好的注释可以提高代码可读性, 坏的注释更可以破坏代码可读性
- 程序员往往不会维护注释, 注释存在的时间越久, 往往跟现有的逻辑偏差越大(很多人只修改代码逻辑而不去更新注释)
- 代码是唯一可信的
- 注释并不能优化糟糕的代码, 不要依赖注释来提高代码可读性, 而是着手于更新代码
- 注释可以写法法律信息,但是要注意不要长篇大论,最好是一个指向详细信息的链接(前提是公司要求必须携带法律信息)
- 如果变量名或者逻辑能够让人直接看懂, 那么注释是多此一举的
- 注释内容可以是: 我为什么这样做, 目的是来对某些复杂或者迫不得已的逻辑进行解释和辩护
- 注释内容可以是说明变量的意义, 但是记住优先看看变量名是否可以优化
- 注释内容可以是警告, 告诉读者不要进行某些危险操作, 前提是你觉得其他人很容易进行误操作
- 注释内容可以是TODO, 但是注意, 针对已经完成的TODO务必将注释删除
- 注释一定是需要有意义的
- 注释并不是一定需要的, 也不能是一个规定(比如规定只要是函数必须有注释)
- 如果逻辑变化, 一定要删除老的注释编写新的注释或者不写, 不能像更新文档一样将多个版本变化堆叠到一起
- 注释的内容不要是XXX在某个时间编写了这个代码或文件,或者其他的归属或署名, git比注释更加可靠, 并且事实上代码总是会不停迭代
- 直接把代码变成注释是非常low的做法, 没有任何用处, 应当被清理掉
- 避免在注释里掺杂HTML, 可读性很差
- 注释只与当前最近的代码关联, 不要离得很远
- 注释要简单和干练
- 注释目的是解释代码, 注释不能让人无法看懂
- 函数头注释不一定需要, 给函数起一个好名字更重要
格式
- 团队的代码风格和格式要求应该保持一致
- 善用代码格式化工具(比如vscode的go插件)
- 代码文件的行数越短, 越利于理解
- 代码文件名应当简单且一目了然, 并且目录的层级可以清楚的表示逻辑的层级
- 代码中的段之间应该有空行进行分隔
- 紧密相关的代码之间不应该有空行
- 变量的声明应该尽可能地靠近他的使用位置
- 函数内部的本地变量应该在函数的顶部出现
- 循环内部的变量在循环内声明
- 如果函数有调用关系, 被调用函数应该紧紧贴在调用函数上方
- 把概念相关的函数放在一起, 它们之间的联系越紧密, 距离也应该越短
- 一行代码的长度不要很长, 最好是100字符以内
- 赋值操作符左右加空格来进行强调
- 运算符加上空格体现出运算顺序
- 水平对齐可以提高整洁度
- 缩进要与逻辑的层级对应
- 团队内的代码风格和规则应该保持一致
posted @
2023-07-11 19:31
ChnMig
阅读(
70)
评论()
编辑
收藏
举报