抛弃代码的坏味道 - 提升代码质量之可读性

[写在开始]代码质量是每个项目都在呼吁的。却是大家都不愿实施的。长期效益诱惑抵抗不了短期收益的现实。目前,它给予的程序猿只有自我满足和成就感。一个深陷老旧系统无法脱身程序员面对代码无奈大家都有耳闻。在追求交付至上的项目,代码质量问题不是团队、项目、公司面临的问题,只属于程序猿。

代码质量

最近花时间整理:我对代码的可读性和可测试性理解。代码质量提升又没有统一标准,如果寻找不可能完成的任务:说服程序猿修改自己的代码,一定包含其中。所以,本文纯属个人理解。

这篇文章分为2部分:代码可读性和代码可测试性。

可读性和可测试是没有统一的量化标准,尤其是新语言不断涌现的。

一切量化指标都有纲领性文件。本文指导性纲领:最为我们熟知的是SOLID, KISS, DRY ...。内容是他们的具体化。

1、注释可读性原则

代码用来维护和阅读的"文档",代码是功能最直接的传达者。而注释是为什么要如此传达的解释。开发模式摒弃注释本身就不利于代码可读性和可维护性。注释编写包括如下几个方面。

  • 注释内容
    注释描述为什么code,而不是code是什么。最好描述code是什么的是:代码实现,而不是字面的描述。而为什么这么做,是代码无法描述的。
  • 注释变化
    保持注释持续更新,使之与当前实现匹配。不匹配的注释非但不是理解code的助推器,反而是阻燃剂。
  • 不要添加无意义的注释
  • 不增加的关于调试信息描述
getUTCTime()// debug:获取UTC 用于记录耗时
  • 不要无意义的注释
new Date() // 生成Date 信息

以上反例表明:能用代码解决的就不要加注释。

  • 注释跟踪
    注释跟踪:记录代码注释历史。既为了追溯,也便于阅读者和修改方进行信息传递。

2、通用可读性原则

通用原则指对变量、方法、以及类都适用的原则。

  • 格式规范化
    项目规范代码格式化内容。诸如 空格缩进、方式修饰符使用、{}、if ...else.. 等控制关键字使用、驼峰式命名或者匈牙利命名等。规划化的格式才是代码可读性的基础。
  • 命名规范化
    • 英文使用规划化
      比如:修复对应的英文, 包括repair fix ...。如何选择合适的词汇来描述。尽量使用软件行业专用词汇,对老旧系统即使无法统一,也不要出现相同术语而使用不同的词汇。
    • 不要在命名中嵌入数据类型尤其是函数中
//功能是把日期转换成Int,这是不建议的方式,*函数的返回类型*标识的Int
//函数中加入类型是画蛇添足
.... //伪代码
  getDate2Int() 
  • 使用好的框架
    避免重复造轮子,使用好的框架。在每个领域都有优秀的开源框架。如数据库连接池管理库Druid HikariCP DBCP...,线程池管理框架、支持缓存的Redis,CacheMem。优秀的框架即战力很强大,我们都值得拥有。

  • 避免重复的代码
    坚持DRY(don't repeat yourself.)原则一百年不动摇。

  • 代码模块化
    模块化没有直接受益,我只能说对可读性而言太重要
    模块化和组件化设计意味着最小知识、技能和依赖在系统中,防止教条出现、构建抽象层、避免设计复杂的相互依赖、功能独立性等....

  • 代码review原则

让review真正运作起来。review代码是检验可读性最佳标准。review过程中的,信息沟通和经验传递是提升编码水平和形成习惯的最佳途径。

  • 给代码以时间

套用大刘的这句话。试想几个月后甚至更久后,我们回看代码,若逻辑不清晰、结构待调整,说明代码可读性有提升空间。如果你意识到代码可读性差,也表明个人能力提升。所以一切交给时间吧。

3、函数可读性原则

  • 函数简短化
    受过长函数苦的,就不要让其它人继续受累。我们不武断提倡每个函数少于5行,但绝不让它们超过 50行。
  • 参数列表简短化
    限制参数的长度, 3~5个为宜。过长的参数列表,往往意味这超长的函数和复杂代码逻辑。
  • 参数最小化原则
    遵守参数最小化原则,即不需要的参数不用传入。参数没有最小化,是代码腐化的主要原因。
calss Person(name: String, addr: String, age: Int, sex: String)
def isFemale(person: Person): Boolean = {
    ....
}

上例中判断是否是性别,只用传入sex就满足功能要求,传入的参数是Person 违背参数最小化原则。

  • 代码执行逻辑统一
    我们功能实现时,过程控制条件太多,且没有统一逻辑。代码经常看到if ...else...满天飞,但细读下来各个分支逻辑并不同。如下正例:都是根据小时条件生成结果。反例则可读性不高。
{
   DateTime time = DateTime.Now;
   if (time.Hour >= 0 && time.Hour < 6)
   {
       return "Night";
   }
   if (time.Hour >= 6 && time.Hour < 12)
   {
       return "Morning";
   }
  ....
   return "Evening";
}
//反例,lineitem card并不是一个维度内容
if (lineItem != null && (lineItem.quantity > 0 && customer.isActive() && (card.expirityYear > 2010 || card.type == AMX)) || couponCode.isValid()) {
// details..
}
//封面图是又一可读性反证
  • 代码有效性
    义无反顾的删除过期代码和调试代码。减少无用日志输出。让代码有意义

  • 单一职责
    就是我们常说的SRP。就函数而言,保证以上原则SRP就满足了。

3、类可读性原则

  • 数据类和实现类分离
    数据的提供者和使用者分离。避免类中存储数据,外部数据在类生成时传入,类只负责对数据的操作。

  • 不同类提供不同接口
    如果内部相似的类,对外却提供不同的接口,让其接口统一:创建接口层,etc.保持内外提供相同接口。

  • 简化初始化逻辑
    面向对象编程,多重继承让类初始化逻辑异常复杂。为了简化逻辑,类初始化逻辑耦合尽量小。

  • 减少类接口暴露数量
    是时候使用访问级别修饰符限定访问权限,和简化对外接口。

  • 明确函数归属原则
    若函数实现依赖其它类,那么它属于依赖函数,而不是当前类。

  • 隔离依赖
    常见场景:依赖库如无法进行修改,为了将来便于使用新依赖库,构造接口类进行隔离。防止对旧有库的行为使用。

  • 废弃任何不必须类
    任何类都会增加项目的复杂度,多余功能类进行废弃和合并

  • 重构类
    如果一个类修改,同时要修改若干其它类,对该类进行进行重构。保证修改仅限在一个类中。新需求引入稳定系统,可行性方案越少,越利于维护。系统腐化的风险越小。

后续

代码可读性个人性很强。不同的阶段,对美的理解不同。就如同欣赏NBA比赛,时间久了,马刺黑,也会成为刺蜜。品味是个很玄幻的东西,要个人努力和时间积累。

一切的提升源于认识深入。

喜欢就冒个泡或赏个赞哈

推荐阅读更多精彩内容