如何代码整洁
什么是整洁的代码
代码的整洁和代码能否运行无关,作为开发者每天都要花费大量时间去阅读代码,如果代码的整洁度不够,会导致浪费大量的时间和生产力,并且糟糕的代码会导致整个代码库的死亡,因为没有人能完全理解掌控。
归根结底,它是可读和可理解的有意义的代码。这可以减轻必须经历的认知负担,认知负担会更小,整洁的代码确保项目的可维护性,是别人可以理解代码,作为开发者应该把自己的代码当做一篇好故事,一篇文章。确保代码是易于理解的,有趣的。
关键痛点&如何去做
痛点
命名:变量、函数、类
代码格式化:格式化、注释
函数:代码量,参数数量
条件语句:深嵌套、没有错误处理
类、数据结构:没有保持区别和数据结构分隔、臃肿的类
解决方案
- 规则和概念
- 模式和原则
- 测试驱动开发
强类型语言能够帮你预防错误和提高代码的可读性,但这不是必须的,不使用类型一样可以编写出可读性强的代码
干净的架构和干净的代码的关系:干净的代码是专注于如何编写代码,如何去编写一段程序或一个文件,而干净的架构更加专注于架构的依赖,数据的存储,代码的分发,但是要实现干净的架构同样也依赖干净的代码
编写整洁的代码是一个渐进的过程,没有人能一开始就编写出最好的代码,随着项目的不断修改,迭代,功能的增加,需要不断添加新功能和旧功能一起实施,同时也是一个不断重构的过程,重构是一件非常正常的事,是在开发中要做的事情,不断地更新重构和质疑代码,明天就会有更好的代码,也会大大加快项目构筑的速度,如果一直写脏代码,随着时间项目将逐渐难以维护,添加新功能和修复bug也会越来越难
命名
为什么命名重要
名字应该有意义
对一个事情有好的命名可以在不了解全部细节的情况知道功能
命名形式
下划线命名法、蛇形命名法 | 驼峰式命名法 | 帕斯卡命名法、大驼峰命名法 | 短横线命名 | |
---|---|---|---|---|
范例 | snake_case | camelCase | PascalCase | kebap-case |
规则 | 下划线连接,全部小写 | 首单词小写,后续全部单词首字母大写 | 所有单词首字母大写 | 短横线连接,全部小写 |
适用范围 | 变量、函数、方法 | 变量、函数、方法 | 类 | 自定义html元素 |
命名形式只是约定的惯例,和本身代码质量没有关系,用哪种都可以,但是得确保和语言本身契合,这也是需要注意的一点
如何命名
变量、常量、属性:微小的数据容器,使用带形容词的名词或短语
// Object
user
authenticatedUser
database
sqlDatabase
// Number or String
name
firstName
age
// Boolean
isActive
isActiveUser
LoggedIn
命名有的时候短好,有的时候长好,这取决于有没有把这个名字背后的事物描述清楚
函数、方法:不应该带数据,是具体逻辑的抽离,使用带形容词的动词或短语
// 进行操作的函数
getUserByEmail()
response.send()
// 取布尔值的函数
emailIsValid()
purchase.isPaid()
函数的命名应该提供更多的细节来介绍功能
类:创造对象,实例化某些事物,使用名词与名词或短语连用,多个名词互相组合,首字母大写
class Customer
class RequestBody
class DatabaseManager
常见问题
- 不要在命名中包含多余信息,命名是为了知道主要功能,而不是描述一切
- 避免俚语,不清楚的缩写和虚假信息
- 避免使用过于相似的名字
- 应该保持命名风格的统一
注释
有的时候注释是冗余的信息,如果代码能够不需要注释就能很好的阅读,注释反而会使理解变的困难
代码的注释如果太长时间不需要这段代码应该删除而不是注释,如果需要应该通过版本控制系统来找回,不需要害怕删除
如果可以,最好尽量控制项目中出现少的注释,因为多余的文字影响阅读
什么是好的注释
- 法律信息注释
- 对行为解释的注释
- 警告某些行为的注释
- todo list 注释
- 文档注释,用于解释一些功能,这一点取决于项目类型
格式化
可以提高代码的可读性并且帮助我们在代码中传递信息
垂直格式化:代码文档自上而下的整体结构
- 大文件:代码应该很容易去阅读,避免从上到下多处进行跳跃,如果代码中涉及许多概念,应该将其拆分到多个文件中去,保持文件的简短可读
- 单文件多概念:使用空行去分割每部分代码,而相似概念的内容则可以归类在一起不需要分隔
- 密切相关概念的联系:彼此具有联系的代码应该尽可能靠近放,增强阅读联系
水平格式化:单行或多行代码的结构
- 避免需要水平滚动的,很长不可读的句子,应该使用缩进和换行还改善
- 中断长句子,部分过长的部分可以使用const 来保存后调用
- 避免强描述性的过长命名
函数
参数
最大程度的减少函数的参数,参数越多,越难以调用,参数的顺序和意义都会令人困惑,会导致阅读更多的代码
在调用时,最方便的是不需要参数的函数,参数最大能接受3个,非常不建议超过3个参数
如果原函数需要不相关的不同参数,应该根据参数的意义拆分函数,使每个函数的功能单一且不需要过多参数
如果有许多相关的参数,因为参数的顺序会限制内部的逻辑,所以应该将所有参数打包成对象传入来避免这样的问题,因为使用对象可以解决强顺序引起的不便利
避免输出参数,不应该将参数传入进行修改后传出,如果需要添加某些数据,对于函数的名称和参数应该做修改,使之更符合职能
抽象层级
在函数的编写中应该根据函数的功能来定义抽象层级,将大量代码的函数进行拆分为许多更低抽象层级的函数,保持每个函数的可读性和体积
在同一个函数中不要混入不同级别的抽象概念
拆分合并规则:
有着相同功能的函数进行合并
比周围代码需要更多解释的概念需要拆分
DRY原则 - don’t repeat youself
不要总是重复写相似的代码,拒绝复制粘贴,使用封装一处修改处处完善,但是拆分代码不是盲目的,过度拆分也不利于提高阅读性 ,下面的一些情况不需要拆分
- 仅仅只是重命名操作
- 寻找新函数将花费比读取代码更长的时间
- 没有比较好的名字来命名函数
保持纯函数
纯函数:对于相同的输入产生相同的输出,输出是100%可以预先确定的。
副作用:副作用是一种操作,它不仅是对函数输入输出起作用,还有可能改变整个系统/程序状态。副作用并不是总是坏的,我们在程序中确实需要他们,但是应该避免意外的副作用
结构控制
创建守卫
使用if语句制造代码守卫,对于流程中的错误做到快速中断,避免其他代码继续执行,把多层嵌套的if判断解脱出来使之变成多层守卫。
if (index == this.current) {
return
}
抛出错误
抛出+处理错误可以替换if语句,并导致更集中的函数
如果某些事是错误的,就抛出错误,善于使用现在语言的内置错误机制
使用工厂函数和多态
对于不同条件下的函数逻辑,可以方法内拆分成多个函数,使用多态来分别处理不同的情况
对象和数据结构
对象可能包含了私有属性,并且允许使用公开api来操作对象
数据结构只是简单的对象,并且内部是公开的,没有方法和api
凝聚力
如果一个类中的数据和方法关联性不强,或者方法之间没有关联使用,这样的类只是数据和方法的集合,这就是凝聚力差的代码
德米特(demeter)定律:模块不应该知道其操作的对象的内部细节。 如果代码依赖于特定对象的内部细节,则很有可能一旦该对象的内部发生更改,它就会被破坏。
SOLID原则:
- S:single responsibility principle 单一职责原则
- 一个类不应该因为一个以上的原因去改变状态
- O: open-closed principle 开闭原则
- 开放用于扩展,但因修改而关闭,这种可扩展性保证了小类的自动进行,否则随着功能的增加大类会变得臃肿,
- L: liskov substitution principle 利斯科夫替代原则
- 对象应该是可以被子类实例替换的,而不会影响任何行为。如果一个类继承自另一个类,那么子类应该支持父类的所有方法,从最初的应该正确的建立数据模型。这有助于编写良好可扩展的代码
- I: interface segregation principle 接口分隔原则
- 设置许多特定的客户端接口比设置一个通用接口要好
- D: dependency inversion principle 依赖倒置原则
- 代码应该依赖于抽象而不是一些在业务中外部的不确定因素,不应该让一些固定的业务写到抽象里
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)