leader:你的代码太烂了我根本看不懂

一、可读性的重要性

编程有很大一部分时间是在阅读代码,不仅要阅读自己的代码,而且要阅读别人的代码。因此,可读性良好的代码能够大大提高编程效率。

可读性良好的代码往往会让代码架构更好,因为程序员更愿意去修改这部分代码,而且也更容易修改。

只有在核心领域为了效率才可以放弃可读性,否则可读性是第一位。

二、用名字表达代码含义

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

单词可替代单词
senddeliver、dispatch、announce、distribute、route
findsearch、extract、locate、recover
startlaunch、create、begin、open
makecreate、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 不包含尾部。

四、良好的代码风格

适当的空行和缩进。

排列整齐的注释:

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;
}

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

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

十一、一次只做一件事

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

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

十二、用自然语言表述代码

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

十三、减少代码量

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

多用标准库实现。

参考资料

  • Dustin, Boswell, Trevor, 等. 编写可读代码的艺术 [M]. 机械工业出版社, 2012.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java写的MySQL数据库备份和恢复代码:

1.MySQL数据库备份和恢复&#xff0c;java代码实现&#xff1a;详情见下面&#xff1a; package com.spring.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOExce…

leetcode541. 反转字符串 II(字符串一律用py秒杀)

给定一个字符串 s 和一个整数 k&#xff0c;你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。 如果剩余字符少于 k 个&#xff0c;则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个&#xff0c;则反转前 k 个字符&#xff0c;其余字符保持原样…

leetcode628. 三个数的最大乘积(简单,我觉得你不会)

给你一个整型数组 nums &#xff0c;在数组中找出由三个数组成的最大乘积&#xff0c;并输出这个乘积。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;6 示例 2&#xff1a; 输入&#xff1a;nums [1,2,3,4] 输出&#xff1a;24 示例 3&#xff1a; …

leetcode453. 最小操作次数使数组元素相等(贼难的简单题)

给定一个长度为 n 的 非空 整数数组&#xff0c;每次操作将会使 n - 1 个元素增加 1。找出让数组所有元素相等的最小操作次数。 示例&#xff1a; 输入&#xff1a; [1,2,3] 输出&#xff1a; 3 解释&#xff1a; 只需要3次操作&#xff08;注意每次操作会增加两个元素的值&am…

leetcode设计链表,非常工整的实现你值得拥有

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性&#xff1a;val 和 next。val 是当前节点的值&#xff0c;next 是指向下一个节点的指针/引用。如果要使用双向链表&#xff0c;则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所…

leetcode1669. 合并两个链表

给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。 请你将 list1 中第 a 个节点到第 b 个节点删除&#xff0c;并将list2 接在被删除节点的位置。 下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 示例 1&#xff…

leetcode1721. 交换链表中的节点

给你链表的头节点 head 和一个整数 k 。 交换 链表正数第 k 个节点和倒数第 k 个节点的值后&#xff0c;返回链表的头节点&#xff08;链表 从 1 开始索引&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[1,4,3,2,5] 示例 2&…

《一天聊一个设计模式》 单例

我是兔兔rabbit&#xff0c;关注我吧&#xff0c;给自己每天的进步找一个机会和理由&#xff0c;不要被无效信息淹没 单例&#xff08;Singleton&#xff09; Intent 确保一个类只有一个实例&#xff0c;并提供该实例的全局访问点。 Class Diagram 使用一个私有构造函数、一…

Eclipse如何卸载插件

Help ---> About Eclipse -->Installation details--->选中你的插件-->Uninstall..

leetcode1502. 判断能否形成等差数列(小学生难度)

给你一个数字数组 arr 。 如果一个数列中&#xff0c;任意相邻两项的差总等于同一个常数&#xff0c;那么这个数列就称为 等差数列 。 如果可以重新排列数组形成等差数列&#xff0c;请返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#x…

leetcode1528. 重新排列字符串

给你一个字符串 s 和一个 长度相同 的整数数组 indices 。 请你重新排列字符串 s &#xff0c;其中第 i 个字符需要移动到 indices[i] 指示的位置。 返回重新排列后的字符串。 示例 1&#xff1a; 输入&#xff1a;s "codeleet", indices [4,5,6,7,0,2,1,3] 输出…

leetcode976. 三角形的最大周长(又是你得不到的简单题)

给定由一些正数&#xff08;代表长度&#xff09;组成的数组 A&#xff0c;返回由其中三个长度组成的、面积不为零的三角形的最大周长。 如果不能形成任何面积不为零的三角形&#xff0c;返回 0。 示例 1&#xff1a; 输入&#xff1a;[2,1,2] 输出&#xff1a;5 示例 2&…

《一天聊一个设计模式》备忘录

备忘录&#xff08;Memento&#xff09; Intent 在不违反封装的情况下获得对象的内部状态&#xff0c;从而在需要时可以将对象恢复到最初状态。 Class Diagram Originator&#xff1a;原始对象Caretaker&#xff1a;负责保存好备忘录Memento&#xff1a;备忘录&#xff0c;…

《一天聊一个设计模式》 策略

9. 策略&#xff08;Strategy&#xff09; Intent 定义一系列算法&#xff0c;封装每个算法&#xff0c;并使它们可以互换。 策略模式可以让算法独立于使用它的客户端。 Class Diagram Strategy 接口定义了一个算法族&#xff0c;它们都实现了 behavior() 方法。Context 是…

如何在eclipse jee中创建Maven project并且转换为Dynamic web project

转自&#xff1a;http://www.javaniu.com/maven-jee-dynamic-web-project.htm 注意:该文档只针对以下eclipse版本&#xff0c;如图 一.在eclipse的官方站点下载eclipse jee版本,地址http://www.eclipse.org/downloads/download.php?file/technology/epp/downloads/release/ind…

《一天聊一个设计模式》 抽象工厂

4. 抽象工厂&#xff08;Abstract Factory&#xff09; Intent 提供一个接口&#xff0c;用于创建 相关的对象家族 。 Class Diagram 抽象工厂模式创建的是对象家族&#xff0c;也就是很多对象而不是一个对象&#xff0c;并且这些对象是相关的&#xff0c;也就是说必须一起…

leetcode1047. 删除字符串中的所有相邻重复项(栈的日常应用)

给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&#xff0c;直到无法继续删除。 在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。 示例&#xff1a; 输入&#xf…

算法题的输入大总结

赶紧收藏吧&#xff0c;小白必备知识了 本文以求和为例 多组输入&#xff0c;每组输入共一行&#xff0c;包括两个整数A, B Sample Input 1 2 12 24 400 500 Sample Output 3 36 900 import java.util.Scanner; public class Main {public static void main(String[] args) …

精华Java问题总结

当时在网上汇总了不知多少面试和基础题&#xff0c;弄了个精华总结。 1、一个".java"源文件中是否可以包括多个类&#xff08;不是内部类&#xff09;&#xff1f;有什么限制&#xff1f; 可以有多个类&#xff0c;但只能有一个public的类&#xff0c;并且public的类…

复习Java的精华总结

小白和老手都应该看看的总结 输入 java.util.Scanner 是 Java5 的新特征&#xff0c;我们可以通过 Scanner 类来获取用户的输入。 下面是创建 Scanner 对象的基本语法&#xff1a; Scanner s new Scanner(System.in); 使用方法如下&#xff1a; //对应类型用对应的方法接…