最近软件工程课程强调了一些编码规范,觉得很有必要记录下来;从而在以后的编码过程中养成良好的编码习惯。
(1)不能以下划线或美元符号开始或结束 (2)类名使用大驼峰(UpperCamelCase)风格 (3)方法名、参数名、成员变量、局部变量都统一使用小驼峰(lowerCamelCase)风格 (4)常量命名全部大写,单词间用下划线隔开 (5)包名统一使用小写
但是,不同语言之间的习惯也有所不同,如Python和Java
驼峰命名法近年来越来越流行,在java平台、Microsoft Windows以及许多新的函数库中使用得较多。 驼峰命名法分为小驼峰法和大驼峰法两种。小驼峰法是指第一个单词以小写字母开始,从第二个单词开始以后的每个单词的首字母都采用大写字母。变量名一般用小驼峰法书写。 大驼峰法的第一个单词的首字母也是大写。常用于类名、属性、命名空间等。
(不过,感觉拼音命名还是有一定可取的哈哈哈,英文单词有时反而太长且缩写产生歧义)
public class Pingyin {
//一、拼音本身不能很好的区分动词和名词,
//提交类型的实例应该是名词,而对于方法这个场景,“提交”应该是动词。但从拼音明明本身,无法得知是名词还是动词。
TiJiao tijiao;
void tiJiao(){
}
//二、从可读性来说,拼音的可读性不一定有英文高
//比如以下场景,对比拼音和英文。拼音很长,阅读不方便,而英文一目了然。
//获取指定时间后的所有订单
Object huoQuZhiDingShiJianHuoDeSuoYouDingDan(LocalDateTime time){
return null;
}
Object getOrdersAfter(LocalDateTime time){
return null;
}
//三、拼音无法区分单数和复数
//返回列表的提交应该是复数,返回单个提交,应该是单数,而拼音无法体现。
List huoQuTiJiao(){
return null;
}
TiJiao huoQuTiJiao(){
return null;
}
//对比使用英文单词命名,区分单复数很方便。
List getOrders(){
return null;
}
TiJiao getOrder(){
return null;
}
}
有时候我们想将一些长的命名进行缩写,减少命名长度,但这样会减少可读性。
public class Abbreviation{
//一、不规范的缩写,影响可读性。
//generateId的缩写
void genId(){
}
//startActivity的缩写
void startAct(){
}
//可能是percent的缩写,也可能是personal computer的缩写
private double pc;
//二、通用的缩写,可以使用
//如Tcp
void createTcpConnection() {
}
//dns
private String dnsServer;
//k8s
private String k8sService;
// 专业领域
public void dijkstra() {
}
}
虽然第一个方法名将意图表达的很清楚(通过产品id从库存系统获取产品库存),但太过于冗长,在阅读时无法一眼看出方法的具体含义。
优化:方法的参数名为productId,已经表达了通过产品id获取的含义,所以可以去掉; 产品库存理应从库存系统获取,所以“从库存系统”的限定语也可以删除。
优化后,方法名表达的含义一目了然。
//通过产品id从库存系统获取产品库存
public void getProductInventoryFromInventorySystemByProductId(String productId) {
}
获取产品库存
public void getProductInventory(String productId) {
}
如果不好命名,考虑类/方法职责过多,是否需要拆分重构。
根据方法名可以得知,该方法需要检查用户是否存在、更新用户、更新缓存、发送邮件给用户,导致命名过长。
public void updateUserBothDBAndCacheThenSendMail() {
// 检查用户是否存在
// 更新用户
// 更新缓存
// 发送邮件给用户
}
进行拆分:(这其实不光是命名规范了,对 对象的合理分析,对类的合理定义也有助于编码)
// 检查用户是否存在
public void isUserExisted() {
}
// 更新用户
public void updateUserInDB() {
}
// 更新缓存
public void updateUserInCache() {
}
// 发送邮件给用户
public void sendNotification() {
}
禁止使用模糊命名,魔法数字!
//(1)当前日期
// 反例
// 无法得知是什么日期
private LocalDate date;
// 正例
//可以一目了然看出这是当前日期
private LocalDate currentDate;
//(2)每页数据条数
// 反例
private int size;
private int lines;
// 正例
private int pageSize;
private int linesPerPage;
//(3)布尔值
// 反例
private boolean flag;
// 正例
private boolean dataReady;
//(4)数值
// 反例
private static final int THREE = 3;
// 正例
private static final int RETRY_TIMES = 3;
//(5)参数
// 反例
public void copyArray(int[] aArray, int[] bArray) {
for (int i = 0; i < aArray.length; i++) {
bArray[i] = aArray[i];
}
}
// 正例
public void copy(int[] source, int[] target) {
for (int sourceIndex = 0; sourceIndex < source.length; sourceIndex++) {
target[sourceIndex] = source[sourceIndex];
}
}
// 反例
public void printScoreDemoOne() {
int[][] scores = new int[10][5];
for (int i = 0; i < scores.length; i++) {
for (int j = 0; j < scores[i].length; j++) {
int score = scores[i][j];
// xxx
}
}
}
// 正例
public void printScoreDemoTwo() {
int[][] scores = new int[10][5];
for (int userIndex = 0; userIndex < scores.length; userIndex++) {
for (int courseIndex = 0; courseIndex < scores[userIndex].length; courseIndex++) {
int score = scores[userIndex][courseIndex];
// xxx
}
}
}
(1)下面代码,定义了用户token所在缓存的命名空间,因为类名只限定了是命名空间,所以我们需要用比较多的限定来限定命名。
public class Namespace {
public static final String USER_TOKEN_CACHE_NAMESPACE = "userToken";
}
(2)如果有一个UserTokenCache类,已经限定了范围是用户token缓存,这时候进行命名空间声明时,只需要使用namespace即可。
public class UserTokenCache {
private final String namespace = "userToken";
}
回到这条规则,范围越大,说明对明明本身的限定就越少,所以我们在命名的时候需要加更多的限定语。反之,范围越小,我们就可以使用更小的限定语,作用域越小,产生歧义的可能性就越小。
编写程序时,应该防止误导性的命名出现。误导性的命名不仅误导他人,也误导自己。 int[] userList = new int[MAXN]; //存储一组用户
如果用userList表示一个用户列表,但他并不是一个List,而是一个数组,那么这就是一个误导性的命名。
对于List这种有特殊意义的词语,最好在只有该标识符真的是一个List时,才可以在命名中包含它。
不管我们使用何种规约进行命名,在团队内部一定要达成一致,否则在系统中明明将非常混乱
不同的企业、团队有各自的编码规范,不同的程序员也有自己的习惯风格..编写出高效、易读、易维护的代码是有点重要的。
References:
头歌实践教学平台(EduCoder)是信息技术类实践教学平台。(EduCoder)涵盖了计算机、大数据、云计算、人工智能、软件工程、物联网等专业课程。超60000个实训案例,建立学、练、评、测一体化实验环境。https://www.educoder.net/