提高代码可读性的8个技巧

小万哥
• 阅读 354

编程有很大一部分时间是在阅读代码,不仅要阅读自己的代码,而且要阅读别人的代码。因此,可读性良好的代码能够大大提高编程效率。可读性良好的代码往往会让代码架构更好,因为程序员更愿意去修改这部分代码,而且也更容易修改。只有在核心领域为了效率才可以放弃可读性,否则可读性是第一位。

用名字代表代码含义

一些比较有表达力的单词:

单词 可替代单词
send deliver、dispatch、announce、distribute、route
find search、extract、locate、recover
start launch、create、begin、open
make create、set up、build、generate、compose、add、new

使用 i、j、k 作为循环迭代器的名字过于简单,user_i、member_i 这种名字会更有表达力。因为循环层次越多,代码越难理解,有表达力的迭代器名字可读性会更高。

为名字添加形容词等信息能让名字更具有表达力,但是名字也会变长。名字长短的准则是:作用域越大,名字越长。因此只有在短作用域才能使用一些简单名字。

名字不能带来歧义

起完名字要思考一下别人会对这个名字有何解读,会不会误解了原本想表达的含义。

布尔相关的命名加上 is、can、should、has 等前缀。

  • 用 min、max 表示数量范围;
  • 用 first、last 表示访问空间的包含范围;
  • begin、end 表示访问空间的排除范围,即 end 不包含尾部。

提高代码可读性的8个技巧

良好的代码风格

适当的空行和缩进。

排列整齐的注释:

int a = 1;   // 注释
int b = 11;  // 注释
int c = 111; // 注释

语句顺序不能随意,比如与 html 表单相关联的变量的赋值应该和表单在 html 中的顺序一致。

为何编写注释

阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。

不能因为有注释就随便起个名字,而是争取起个好名字而不写注释。

可以用注释来记录采用当前解决办法的思考过程,从而让读者更容易理解代码。

注释用来提醒一些特殊情况。

用 TODO 等做标记:

标记 用法
TODO 待做
FIXME 待修复
HACK 粗糙的解决方案
XXX 危险!这里有重要的问题

如何编写注释

尽量简洁明了:

// The first String is student's name
// The Second Integer is student's score
Map<String, Integer> scoreMap = new HashMap<>();
// Student's name -> Student's score
Map<String, Integer> scoreMap = new HashMap<>();

添加测试用例来说明:

// ...

// Example: add(1, 2), return 3

int add(int x, int y) {
  return x + y;
}

使用专业名词来缩短概念上的解释,比如用设计模式名来说明代码。

提高控制流的可读性

条件表达式中,左侧是变量,右侧是常数。比如下面第一个语句正确:

if (len < 10)
if (10 > len)

只有在逻辑简单的情况下使用 ? : 三目运算符来使代码更紧凑,否则应该拆分成 if / else;

do / while 的条件放在后面,不够简单明了,并且会有一些迷惑的地方,最好使用 while 来代替。

如果只有一个 goto 目标,那么 goto 尚且还能接受,但是过于复杂的 goto 会让代码可读性特别差,应该避免使用 goto。

在嵌套的循环中,用一些 return 语句往往能减少嵌套的层数。

拆分长表达式

长表达式的可读性很差,可以引入一些解释变量从而拆分表达式:

if line.split(':')[0].strip() == "root":
    ...
username = line.split(':')[0].strip()
if username == "root":
    ...

使用摩根定理简化一些逻辑表达式:

if (!a && !b) {
    ...
}
if (!(a || b)) {
    ...
}

变量与可读性

去除控制流变量 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。

boolean done = false;

while (/* condition */ && !done) {
  ...
  if ( ... ) {
    done = true;
    continue;
  }
}
while(/* condition */) {
  ...
  if ( ... ) {
    break;
  }
}

减小变量作用域 。作用域越小,越容易定位到变量所有使用的地方。

JavaScript 可以用闭包减小作用域。以下代码中 submit_form 是函数变量,submitted 变量控制函数不会被提交两次。第一个实现中 submitted 是全局变量,第二个实现把 submitted 放到匿名函数中,从而限制了起作用域范围。

submitted = false;

var submit_form = function(form_name) {
  if (submitted) {
    return;
  }
  submitted = true;
};
var submit_form = (function() {
  var submitted = false;
  return function(form_name) {
    if(submitted) {
      return;
    }
    submitted = true;
  }
}());  // () 使得外层匿名函数立即执行

JavaScript 中没有用 var 声明的变量都是全局变量,而全局变量很容易造成迷惑,因此应当总是用 var 来声明变量。

变量定义的位置应当离它使用的位置最近。

实例解析

在一个网页中有以下文本输入字段:

<input type = "text" id = "input1" value = "a">
<input type = "text" id = "input2" value = "b">
<input type = "text" id = "input3" value = "">
<input type = "text" id = "input4" value = "d">

现在要接受一个字符串并把它放到第一个空的 input 字段中,初始实现如下:

var setFirstEmptyInput = function(new_alue) {
  var found = false;
  var i = 1;
  var elem = document.getElementById('input' + i);
  while (elem != null) {
    if (elem.value === '') {
      found = true;
      break;
    }
    i++;
    elem = document.getElementById('input' + i);
  }
  if (found) elem.value = new_value;
  return elem;
}

以上实现有以下问题:

  • found 可以去除;
  • elem 作用域过大;
  • 可以用 for 循环代替 while 循环;
var setFirstEmptyInput = function(new_value) {
  for (var i = 1; true; i++) {
    var elem = document.getElementById('input' + i);
    if (elem === null) {
      return null;
    }
    if (elem.value === '') {
      elem.value = new_value;
      return elem;
    }
  }
};

抽取函数

工程学就是把大问题拆分成小问题再把这些问题的解决方案放回一起。

首先应该明确一个函数的高层次目标,然后对于不是直接为了这个目标工作的代码,抽取出来放到独立的函数中。

介绍性的代码:

int findClostElement(int[] arr) {
    int clostIdx;
    int clostDist = Interger.MAX_VALUE;
    for (int i = 0; i < arr.length; i++) {
        int x = ...;
        int y = ...;
        int z = ...;
        int value = x * y * z;
        int dist = Math.sqrt(Math.pow(value, 2), Math.pow(arr[i], 2));
        if (dist < clostDist) {
            clostIdx = i;
            clostDist = value;
        }
    }
    return clostIdx;
}

以上代码中循环部分主要计算距离,这部分不属于代码高层次目标,高层次目标是寻找最小距离的值,因此可以把这部分代替提取到独立的函数中。这样做也带来一个额外的好处有:可以单独进行测试、可以快速找到程序错误并修改。

public int findClostElement(int[] arr) {
    int clostIdx;
    int clostDist = Interger.MAX_VALUE;
    for (int i = 0; i < arr.length; i++) {
        int dist = computDist(arr, i);
        if (dist < clostDist) {
            clostIdx = i;
            clostDist = value;
        }
    }
    return clostIdx;
}

并不是函数抽取的越多越好,如果抽取过多,在阅读代码的时候可能需要不断跳来跳去。只有在当前函数不需要去了解某一块代码细节而能够表达其内容时,把这块代码抽取成子函数才是好的。

函数抽取也用于减小代码的冗余。

一次只做一件事

只做一件事的代码很容易让人知道其要做的事;

基本流程:列出代码所做的所有任务;把每个任务拆分到不同的函数,或者不同的段落。

用自然语言表述代码

先用自然语言书写代码逻辑,也就是伪代码,然后再写代码,这样代码逻辑会更清晰。

减少代码量

不要过度设计,编码过程会有很多变化,过度设计的内容到最后往往是无用的。

多用标准库实现。

最后

为了方便其他设备和平台的小伙伴观看往期文章,链接奉上:

牛客知乎开源中国CSDN思否掘金InfoQ简书博客园慕课51CTOhelloworld腾讯开发者社区阿里开发者社区

看完如果觉得有帮助,欢迎点赞、收藏关注

点赞
收藏
评论区
推荐文章
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
JavaScript 代码整洁之道
代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护、升级奠定了良好基础。本文并不是代码风格指南,而是关于代码的可读性、复用性、扩展性探讨。我们将从几个方面展开讨论:1.变量2.函数3.对象和数据结构4.类5.SOLID
Stella981 Stella981
3年前
Kotlin代码检查在美团的探索与实践
背景Kotlin有着诸多的特性,比如空指针安全、方法扩展、支持函数式编程、丰富的语法糖等。这些特性使得Kotlin的代码比Java简洁优雅许多,提高了代码的可读性和可维护性,节省了开发时间,提高了开发效率。这也是我们团队转向Kotlin的原因,但是在实际的使用过程中,我们发现看似写法简单的Kotlin代码,可能隐藏着不容忽视的额外开销。本文剖析了K
Stella981 Stella981
3年前
Intellij IDEA安装阿里代码规范插件
要养成一个好的编码习惯从自己编码开始,对自己代码的合理化命名,编码不仅对自己有好处,而且别人也容易读懂你的代码。所以下载阿里的代码规范插件来约束自己凌乱的代码。阿里规范插件GitHub地址:https://github.com/alibaba/p3cIDEA安装该插件步骤:1.打开IDEA,FileSetteingsPlug
Wesley13 Wesley13
3年前
C++ 注释
C注释程序的注释是解释性语句,您可以在C代码中包含注释,这将提高源代码的可读性。所有的编程语言都允许某种形式的注释。C支持单行注释和多行注释。注释中的所有字符会被C编译器忽略。1include<iostream23/runthisprogramusingthe
Stella981 Stella981
3年前
Linux内核源码分析方法
一、内核源码之我见Linux内核代码的庞大令不少人“望而生畏”,也正因为如此,使得人们对Linux的了解仅处于泛泛的层次。如果想透析Linux,深入操作系统的本质,阅读内核源码是最有效的途径。我们都知道,想成为优秀的程序员,需要大量的实践和代码的编写。编程固然重要,但是往往只编程的人很容易把自己局限在自己的知识领域内。如果要扩展自己知识的广度,我们需要多
京东云开发者 京东云开发者
9个月前
想提高阅读代码的效率?试试这些工具吧!
1.前言程序员间有句名言——“Talkischeap,showmethecode!”源码的确相较于言语更接近程序真实的状态,包含了更多的一手信息。因此,无论是刚开始学习代码的小白还是久经沙场的代码大神,不管是学习优秀的开源项目还是做老项目的重构,代码阅读都
小万哥 小万哥
8个月前
深入了解 Java 方法和参数的使用方法
Java方法简介方法是一块仅在调用时运行的代码。您可以将数据(称为参数)传递到方法中。方法用于执行特定的操作,它们也被称为函数。使用方法的原因重用代码:定义一次代码,多次使用。提高代码的结构化和可读性。将代码分解成更小的模块,易于维护和理解。创建方法方法必
linbojue linbojue
7个月前
建立web前端开发规范的重要性(浅谈前端开发的重要性以及前景分析)
一个好的程序员肯定是要能书写可维护的代码,而不是一次性的代码,怎么能让团队当中的其他人,甚至过一段时间之后的你,再看自己某个时期写的代码,依然能看懂?这就涉及到规范你的代码了。一、规范代码的好处1、从根本上降低开发成本:提高代码整体的可读性、可维护性、可复