2月2
编写干净、易于理解且可维护的代码是每位开发者必须掌握的技能。我们将探讨提高代码质量的最重要原则,并为每个原则提供代码示例。
如何命名变量(以及其他事物)
我们不用内存地址而使用名称的原因是:名称更容易记忆。更重要的是,它们能提供更多关于变量的信息,以便其他人理解其意义。
找到一个好的名字可能需要一些时间,但这将在未来为你和你的团队节省更多时间。而且我确信大多数读者都遇到过这种情况:几个月后再次访问自己的代码时,很难理解之前做了什么。
如何创建有意义的名字
不要用注释来解释变量的用途。如果一个名字需要注释,那么你应该花时间重命名那个变量,而不是写注释。
差:
var d; // elapsed time in days
我见过这种代码无数次。人们普遍认为应该用注释来隐藏代码的混乱,但这是一种常见的误解。除非有充分理由,否则不要使用 x、y、a 或 b 等字母作为变量名(循环变量除外)。
好:
var elapsedTimeInDays;
var daysSinceCreation;
var daysSinceModification;
这些名称要好得多。它们告诉你被测量的内容以及该测量的单位。
避免错误信息
注意那些有特定含义的词语。除非其类型确实是 List,否则不要将一组账户称为 accountList。这个词有特定的含义,可能会导致错误的结论。
即使类型是列表,accounts 也是一个更简单、更好的名称。
差:
var accountList = [];
好:
var accounts = [];
避免噪音词
噪声词是指那些不提供任何关于变量额外信息的词。它们是冗余的,应该被删除。
一些常见的噪声词有:
The (prefix)
Info
Data
Variable
Object
Manager
如果你的类名是 UserInfo,你只需去掉 Info,直接用 User。将类名用 BookData 代替 Book 是毋庸置疑的,因为类本来就是为了存储数据而存在的。
使用发音友好的名称
如果你不会读一个名字,讨论它时就会显得很愚蠢。
差:
const yyyymmdstr = moment().format("YYYY/MM/DD");
好:
const currentDate = moment().format("YYYY/MM/DD");
使用可搜索的名称
避免在代码中使用魔法数字。选择可搜索的、命名的常量。不要为常量使用单个字母的名称,因为它们可能出现在很多地方,因此不易搜索。
差:
if (student.classes.length < 7) {
// Do something
}
好:
if (student.classes.length < MAX_CLASSES_PER_STUDENT) {
// Do something
}
这样更好,因为 MAX_CLASSES_PER_STUDENT 可以在代码的许多地方使用。如果将来需要将其改为 6,我们只需更改常量即可。
糟糕的例子会在读者脑海中产生疑问,比如 7 的重要性是什么?
你也应该使用你语言的常量命名和声明约定,例如 Java 中的 private static final 或 JavaScript 中的 const。
保持一致性
遵循每个概念用一个词的规则。不要在不同的类中对同一操作使用 fetch、retrieve 和 get。选择其中一个并在整个项目中统一使用,这样维护代码库的人员或你的 API 客户可以轻松找到他们正在寻找的方法。
如何编写函数
保持它们简短
函数应该很小,真的很小。它们很少会是 20 行长。一个函数越长,它就越可能做很多事情并产生副作用。
确保它们只做一件事
你的函数应该只做一件事情。如果你遵循这个规则,它们一定会很小。函数所做的一切都应该体现在它的名字中。
有时候很难从函数名来判断它是否做了多件事情。一个很好的检查方法是尝试提取一个具有不同名字的另一个函数。如果你能找到,那就意味着它应该是一个不同的函数。
这可能是本文中最重要的概念,需要一些时间来适应。但一旦你掌握了它,你的代码看起来会更加成熟,而且肯定会更容易重构、理解和测试。
将条件语句封装在函数中
重构条件并将其放入命名函数中是使你的条件语句更易读的好方法。
这是我从学校项目里的一段代码。这段代码负责在 Connect4 游戏的棋盘上插入芯片。
isValidInsertion 方法负责检查列号的有效性,让我们可以专注于插入芯片的逻辑。
public void insertChipAt(int column) throws Exception {
if (isValidInsertion(column)) {
insertChip(column);
boardConfiguration += column;
currentPlayer = currentPlayer == Chip.RED ? Chip.YELLOW : Chip.RED;
} else {
if (!columnExistsAt(column))
throw new IllegalArgumentException();
else if (isColumnFull(column - 1) || getWinner() != Chip.NONE)
throw new RuntimeException();
}
}
如果你感兴趣,这里是 isValidInsertion 的代码。
private boolean isValidInsertion(int column) {
boolean columnIsAvailable = column <= NUM_COLUMNS && column >= 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS;
boolean gameIsOver = getWinner() != Chip.NONE;
return columnIsAvailable && !gameIsOver;
}
如果没有方法,if 条件将如下所示:
if (column <= NUM_COLUMNS
&& column >= 1
&& numberOfItemsInColumn[column - 1] < NUM_ROWS
&& getWinner() != Chip.NONE)
恶心,对吧?我同意。
减少参数
函数应该有两个或更少的参数,越少越好。尽量避免有三个或更多参数。
参数使得函数更难阅读和理解。从测试的角度来看,它们甚至更难,因为它们需要为每个参数组合编写测试用例。
不要使用标志参数
标志参数是一个传递给函数的布尔值参数。根据这个参数的值,会采取两种不同的操作。
例如,假设有一个负责预订音乐会门票的函数,并且有两种类型的用户:高级和普通。你可以编写如下代码:
public Booking book (Customer aCustomer, boolean isPremium) {
if(isPremium)
// logic for premium book
else
// logic for regular booking
}
标志参数天然地违背了单一职责原则。当你看到它们时,应该考虑将函数拆分成两个。
不要有副作用
副作用是你的代码的意外后果。它们可能是改变传递的参数,在按引用传递的情况下,或者可能改变一个全局变量。
关键点在于,它们承诺做另一件事,你需要仔细阅读代码才能注意到副作用。它们可能导致一些棘手的错误。
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
你能看到这个函数的副作用吗?
它在检查密码,但当密码有效时,它还会初始化会话,这是一个副作用。
你可以将函数的名称改为类似 checkPasswordAndInitializeSession,以使这种效果显式化。但当你这样做时,你应该注意到你的函数实际上在处理两件事,你不应该在这里初始化会话。
不要重复自己
代码重复可能是软件中所有邪恶的根源。重复的代码意味着在逻辑发生变化时需要在多个地方进行修改,而且非常容易出错。
使用你的 IDE 的重构功能,每当遇到重复的代码片段时,提取一个方法。
其他
不要在代码注释中留下代码
请勿如此。这一点很严重,因为看到代码的其他人会因为不知道它是否有什么用而不敢删除它。那些被注释掉的代码会停留很长时间。然后当变量名或方法名改变时,它变得无关紧要,但仍然没有人删除它。
干脆删除它。即使它很重要,也有版本控制可以找回。
了解你语言的规范
你应该了解你语言在空格、注释和命名方面的规范。许多语言都有可用的风格指南。
例如,你应该在 Java 中使用 camelCase,但在 Python 中使用 snake_case。在 C#中,你应该将开括号放在新行,但在 Java 和 JavaScript 中,你应该将它们放在同一行。
这些事情因语言而异,没有统一的标准。
结论
编写干净代码并非一蹴而就的技能,它是一种需要时刻牢记这些原则并在编写代码时加以应用的习惯。
感谢您抽出时间阅读,希望对您有所帮助。
来源:Heck's Blog
地址:https://www.heckjj.com/post/680/
转载时须以链接形式注明作者和原始出处及本声明,否则将追究法律责任,谢谢配合!
如何命名变量(以及其他事物)
我们不用内存地址而使用名称的原因是:名称更容易记忆。更重要的是,它们能提供更多关于变量的信息,以便其他人理解其意义。
找到一个好的名字可能需要一些时间,但这将在未来为你和你的团队节省更多时间。而且我确信大多数读者都遇到过这种情况:几个月后再次访问自己的代码时,很难理解之前做了什么。
如何创建有意义的名字
不要用注释来解释变量的用途。如果一个名字需要注释,那么你应该花时间重命名那个变量,而不是写注释。
差:
var d; // elapsed time in days
我见过这种代码无数次。人们普遍认为应该用注释来隐藏代码的混乱,但这是一种常见的误解。除非有充分理由,否则不要使用 x、y、a 或 b 等字母作为变量名(循环变量除外)。
好:
var elapsedTimeInDays;
var daysSinceCreation;
var daysSinceModification;
这些名称要好得多。它们告诉你被测量的内容以及该测量的单位。
避免错误信息
注意那些有特定含义的词语。除非其类型确实是 List,否则不要将一组账户称为 accountList。这个词有特定的含义,可能会导致错误的结论。
即使类型是列表,accounts 也是一个更简单、更好的名称。
差:
var accountList = [];
好:
var accounts = [];
避免噪音词
噪声词是指那些不提供任何关于变量额外信息的词。它们是冗余的,应该被删除。
一些常见的噪声词有:
The (prefix)
Info
Data
Variable
Object
Manager
如果你的类名是 UserInfo,你只需去掉 Info,直接用 User。将类名用 BookData 代替 Book 是毋庸置疑的,因为类本来就是为了存储数据而存在的。
使用发音友好的名称
如果你不会读一个名字,讨论它时就会显得很愚蠢。
差:
const yyyymmdstr = moment().format("YYYY/MM/DD");
好:
const currentDate = moment().format("YYYY/MM/DD");
使用可搜索的名称
避免在代码中使用魔法数字。选择可搜索的、命名的常量。不要为常量使用单个字母的名称,因为它们可能出现在很多地方,因此不易搜索。
差:
if (student.classes.length < 7) {
// Do something
}
好:
if (student.classes.length < MAX_CLASSES_PER_STUDENT) {
// Do something
}
这样更好,因为 MAX_CLASSES_PER_STUDENT 可以在代码的许多地方使用。如果将来需要将其改为 6,我们只需更改常量即可。
糟糕的例子会在读者脑海中产生疑问,比如 7 的重要性是什么?
你也应该使用你语言的常量命名和声明约定,例如 Java 中的 private static final 或 JavaScript 中的 const。
保持一致性
遵循每个概念用一个词的规则。不要在不同的类中对同一操作使用 fetch、retrieve 和 get。选择其中一个并在整个项目中统一使用,这样维护代码库的人员或你的 API 客户可以轻松找到他们正在寻找的方法。
如何编写函数
保持它们简短
函数应该很小,真的很小。它们很少会是 20 行长。一个函数越长,它就越可能做很多事情并产生副作用。
确保它们只做一件事
你的函数应该只做一件事情。如果你遵循这个规则,它们一定会很小。函数所做的一切都应该体现在它的名字中。
有时候很难从函数名来判断它是否做了多件事情。一个很好的检查方法是尝试提取一个具有不同名字的另一个函数。如果你能找到,那就意味着它应该是一个不同的函数。
这可能是本文中最重要的概念,需要一些时间来适应。但一旦你掌握了它,你的代码看起来会更加成熟,而且肯定会更容易重构、理解和测试。
将条件语句封装在函数中
重构条件并将其放入命名函数中是使你的条件语句更易读的好方法。
这是我从学校项目里的一段代码。这段代码负责在 Connect4 游戏的棋盘上插入芯片。
isValidInsertion 方法负责检查列号的有效性,让我们可以专注于插入芯片的逻辑。
public void insertChipAt(int column) throws Exception {
if (isValidInsertion(column)) {
insertChip(column);
boardConfiguration += column;
currentPlayer = currentPlayer == Chip.RED ? Chip.YELLOW : Chip.RED;
} else {
if (!columnExistsAt(column))
throw new IllegalArgumentException();
else if (isColumnFull(column - 1) || getWinner() != Chip.NONE)
throw new RuntimeException();
}
}
如果你感兴趣,这里是 isValidInsertion 的代码。
private boolean isValidInsertion(int column) {
boolean columnIsAvailable = column <= NUM_COLUMNS && column >= 1 && numberOfItemsInColumn[column - 1] < NUM_ROWS;
boolean gameIsOver = getWinner() != Chip.NONE;
return columnIsAvailable && !gameIsOver;
}
如果没有方法,if 条件将如下所示:
if (column <= NUM_COLUMNS
&& column >= 1
&& numberOfItemsInColumn[column - 1] < NUM_ROWS
&& getWinner() != Chip.NONE)
恶心,对吧?我同意。
减少参数
函数应该有两个或更少的参数,越少越好。尽量避免有三个或更多参数。
参数使得函数更难阅读和理解。从测试的角度来看,它们甚至更难,因为它们需要为每个参数组合编写测试用例。
不要使用标志参数
标志参数是一个传递给函数的布尔值参数。根据这个参数的值,会采取两种不同的操作。
例如,假设有一个负责预订音乐会门票的函数,并且有两种类型的用户:高级和普通。你可以编写如下代码:
public Booking book (Customer aCustomer, boolean isPremium) {
if(isPremium)
// logic for premium book
else
// logic for regular booking
}
标志参数天然地违背了单一职责原则。当你看到它们时,应该考虑将函数拆分成两个。
不要有副作用
副作用是你的代码的意外后果。它们可能是改变传递的参数,在按引用传递的情况下,或者可能改变一个全局变量。
关键点在于,它们承诺做另一件事,你需要仔细阅读代码才能注意到副作用。它们可能导致一些棘手的错误。
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
你能看到这个函数的副作用吗?
它在检查密码,但当密码有效时,它还会初始化会话,这是一个副作用。
你可以将函数的名称改为类似 checkPasswordAndInitializeSession,以使这种效果显式化。但当你这样做时,你应该注意到你的函数实际上在处理两件事,你不应该在这里初始化会话。
不要重复自己
代码重复可能是软件中所有邪恶的根源。重复的代码意味着在逻辑发生变化时需要在多个地方进行修改,而且非常容易出错。
使用你的 IDE 的重构功能,每当遇到重复的代码片段时,提取一个方法。
其他
不要在代码注释中留下代码
请勿如此。这一点很严重,因为看到代码的其他人会因为不知道它是否有什么用而不敢删除它。那些被注释掉的代码会停留很长时间。然后当变量名或方法名改变时,它变得无关紧要,但仍然没有人删除它。
干脆删除它。即使它很重要,也有版本控制可以找回。
了解你语言的规范
你应该了解你语言在空格、注释和命名方面的规范。许多语言都有可用的风格指南。
例如,你应该在 Java 中使用 camelCase,但在 Python 中使用 snake_case。在 C#中,你应该将开括号放在新行,但在 Java 和 JavaScript 中,你应该将它们放在同一行。
这些事情因语言而异,没有统一的标准。
结论
编写干净代码并非一蹴而就的技能,它是一种需要时刻牢记这些原则并在编写代码时加以应用的习惯。
感谢您抽出时间阅读,希望对您有所帮助。
来源:Heck's Blog
地址:https://www.heckjj.com/post/680/
转载时须以链接形式注明作者和原始出处及本声明,否则将追究法律责任,谢谢配合!
Sublime Text
限时活动,免费白嫖Nan



