一些能够提升代码质量的编程规范

算是读书笔记吧

极客时间--设计模式之美


命名

命名的一个原则就是以能准确达意为目标,长短主要取决于给谁看。
学会换位思考,假设自己不熟悉这块代码,从代码阅读者的角度去考量命名是否足够直观。

Github

或者有两个网站可以推荐:
CODELF可以搜索到很多Git上的代码命名
Free App可以搜索到很多App以及Web国际化翻译

长度

  1. 默认的、大家都比较熟知的词,可以用缩写
  2. 作用于小的,比如临时变量用短命名
  3. 作用域比较大的,比如全局变量、类名用长的命名

这里的短,并不是让你用a1、a2这种无法辨认的名字
而是比如用sec 表示 second、str 表示 string、num 表示 number、doc 表示 document这样的缩写。

利用上下文简化命名

public class User {
  private String userName;
  private String userPassword;
  private String userAvatarUrl;
  //...
}
//利用上下文简化为:
User user = new User();
user.getName(); // 借助user对象这个上下文
public void uploadUserAvatarImageToAliyun(String userAvatarImageUri);
//利用上下文简化为:
public void uploadUserAvatarImageToAliyun(String imageUri);

命名可读

选用的单词。最优先的是能大家看懂、实在不行最起码要能读出来

命名可搜索

比如数组是用array,还是list。插入是用insertXXX,还是addXXX。
这个更多的依赖统一规约,能减少很多不必要的麻烦

特殊前缀

抽象类、接口、常量的前缀在项目里能够统一


注释

类的注释要写得尽可能全面、详细。
让人一眼就能看明白这个类的大致作用和实现思路,而不需要进去查阅大量的代码

public函数/接口声明

如果他会给其他业务方使用,写上注释吧。是个好习惯

普通函数声明

通常我们可以通过良好的命名替代注释
但是对于一些特殊细节,比如参数和错误的特殊描述,需要进行注释

函数内部

函数内部的通常也是通过良好的命名以及解释性变量让代码自解释

但是对于内部比较复杂的函数,总结性注释可以很好的对函数进行拆分,使逻辑更清晰

public boolean isValidPasword(String password) {
  // check if password is null or empty
  if (StringUtils.isBlank(password)) {
    return false;
  }

  // check if the length of password is between 4 and 64
  int length = password.length();
  if (length < 4 || length > 64) {
    return false;
  }
    
  // check if password contains only a~z,0~9,dot
  for (int i = 0; i < length; ++i) {
    char c = password.charAt(i);
    if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.')) {
      return false;
    }
  }
  return true;
}

注释的维护成本

注释本身有一定的维护成本,所以并非越多越好。撰写精炼的注释,也是一门艺术


函数、类的大小

大小这个东西很主观,就像对于“放盐少许”中的“少许”,即便是大厨也很难告诉你一个特别具体的量值。

函数代码行数,不要超过一个显示屏的垂直高度

一般来讲大概是50

超过一屏之后,在阅读代码的时候,为了串联前后的代码逻辑,就可能需要频繁地上下滚动屏幕,阅读体验不好不说,还容易出错

类的大小,以不影响阅读为准

当一个类的代码读起来让你感觉头大了,实现某个功能时不知道该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,这就说明类的行数过多了。

善用空行分割单元块

对于比较长的函数,如果

  1. 逻辑上可以分为几个独立的代码块
  2. 不方便将这些独立的代码块抽取成小函数的情况下:

通过添加空行的方式,让这些不同模块的代码之间,界限更加明确。


一些具体的函数编写技巧

这些技巧,可以显著的提升同一份代码的质量

将代码拆分成更小的单元块

要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节
让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性,聚焦于业务


// 重构前的代码
public void invest(long userId, long financialProductId) {
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(date);
  calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
  if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
    return;
  }
  //...
}

// 重构后的代码:提炼函数之后逻辑更加清晰
public void invest(long userId, long financialProductId) {
  if (isLastDayOfMonth(new Date())) {
    return;
  }
  //...
}

public boolean isLastDayOfMonth(Date date) {
  Calendar calendar = Calendar.getInstance();
  calendar.setTime(date);
  calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
  if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
   return true;
  }
  return false;
}

避免函数参数过多

函数包含 3、4 个参数的时候还是能接受的
再多,就需要重构代码进行优化

通常我们会将参数封装成对象


public void postBlog(String title, String summary, String keywords, String content, String category, long authorId);

// 将参数封装成对象
public class Blog {
  private String title;
  private String summary;
  private String keywords;
  private Strint content;
  private String category;
  private long authorId;
}
public void postBlog(Blog blog);

不过,这样在使用的时候,不同环境下的参数定义可能随着迭代就不那么明确了,有利有弊吧。

迭代时,bool值传入方法作为隔离依据

这个情况对于很多开发人员都很常见,尤其在修改迭代他人代码的时候。
这种打补丁的方式,随着补丁的增多,对项目也是毁灭性的。

void configXXXX() {
   //基本操作...
}

//迭代后
void configXXXX(isModeA , isModeB) {
   //基本操作...
  if (isModeA) {
    //特殊操作...
  }
  //基本操作...
  if (isModeB) {
    //特殊操作
  }

  //基本操作...
  if (isModeA && !isModeB) {
    //特殊操作...
  } else if (isModeA){
    //特殊操作...
  }
  //基本操作...
}

函数设计要职责单一

不要设计一个大而全的函数,而在函数的内部做一大堆的盘点。
最起码,具体的功能实现函数不要耦合其他职责的逻辑

比如字符串的校验时,电话、用户名、邮箱三者的实现逻辑(将来)很可能是不同的,要分开定义。

public boolean checkUserIfExisting(String telephone, String username, String email)  { 
  if (!StringUtils.isBlank(telephone)) {
    User user = userRepo.selectUserByTelephone(telephone);
    return user != null;
  }
  
  if (!StringUtils.isBlank(username)) {
    User user = userRepo.selectUserByUsername(username);
    return user != null;
  }
  
  if (!StringUtils.isBlank(email)) {
    User user = userRepo.selectUserByEmail(email);
    return user != null;
  }
  
  return false;
}

// 拆分成三个函数
public boolean checkUserIfExistingByTelephone(String telephone);
public boolean checkUserIfExistingByUsername(String username);
public boolean checkUserIfExistingByEmail(String email);

移除过深的嵌套层次

通常是if-else、switch-case、for 循环过度嵌套导致
我们可以最开始按照执行顺去去编写多层代码,功能开发完毕函数稳定之后,再回过头来着手移除

  • 去掉多余的 else 语句
public double caculateTotalAmount(List<Order> orders) {
  if (orders == null || orders.isEmpty()) {
    return 0.0;
  } else { // 此处的else可以去掉
    double amount = 0.0;
    for (Order order : orders) {
      if (order != null) {
        amount += (order.getCount() * order.getPrice());
      }
    }
    return amount;
  }
}
  • 去掉多余的if
public List<String> matchStrings(List<String> strList,String substr) {
  List<String> matchedStrings = new ArrayList<>();
  if (strList != null && substr != null) {
    for (String str : strList) {
      if (str != null) { // 跟下面的if语句可以合并在一起
        if (str.contains(substr)) {
          matchedStrings.add(str);
        }
      }
    }
  }
  return matchedStrings;
}
  • 提前退出嵌套
// 重构前的代码
public List<String> matchStrings(List<String> strList,String substr) {
  List<String> matchedStrings = new ArrayList<>();
  if (strList != null && substr != null){ 
    for (String str : strList) {
      if (str != null && str.contains(substr)) {
        matchedStrings.add(str);
        // 此处还有10行代码...
      }
    }
  }
  return matchedStrings;
}

// 重构后的代码:使用return和continue提前退出
public List<String> matchStrings(List<String> strList,String substr) {
  List<String> matchedStrings = new ArrayList<>();
  if (strList == null || substr == null){ 
    return matchedStrings;
  }
  for (String str : strList) {
    if (str == null || !str.contains(substr)) {
      continue; 
    }
    matchedStrings.add(str);
    // 此处还有10行代码...
  }
  return matchedStrings;
}
  • 将部分嵌套逻辑封装成函数调用

对于无法通过逻辑顺序进行优化的代码,可以通过把局部功能封装成小的函数来减轻主函数的嵌套层级。


// 重构前的代码
public List<String> appendSalts(List<String> passwords) {
  if (passwords == null || passwords.isEmpty()) {
    return Collections.emptyList();
  }
  
  List<String> passwordsWithSalt = new ArrayList<>();
  for (String password : passwords) {
    if (password == null) {
      continue;
    }
    if (password.length() < 8) {
      // ...
    } else {
      // ...
    }
  }
  return passwordsWithSalt;
}

// 重构后的代码:将部分逻辑抽成函数
public List<String> appendSalts(List<String> passwords) {
  if (passwords == null || passwords.isEmpty()) {
    return Collections.emptyList();
  }

  List<String> passwordsWithSalt = new ArrayList<>();
  for (String password : passwords) {
    if (password == null) {
      continue;
    }
    passwordsWithSalt.add(appendSalt(password));
  }
  return passwordsWithSalt;
}

private String appendSalt(String password) {
  String passwordWithSalt = password;
  if (password.length() < 8) {
    // ...
  } else {
    // ...
  }
  return passwordWithSalt;
}

解释性变量

  • 常量取代魔法数字

public double CalculateCircularArea(double radius) {
  return (3.1415) * radius * radius;
}

// 常量替代魔法数字
public static final Double PI = 3.1415;
public double CalculateCircularArea(double radius) {
  return PI * radius * radius;
}
  • 解释性变量来解释复杂表达式
if (date.after(SUMMER_START) && date.before(SUMMER_END)) {
  // ...
} else {
  // ...
}

// 引入解释性变量后逻辑更加清晰
boolean isSummer = date.after(SUMMER_START)&&date.before(SUMMER_END);
if (isSummer) {
  // ...
} else {
  // ...
} 
  • 多个复合条件的bool,最好加上注释
// ((团购 &&  非折扣 && 有销售价格) || 有团购列表)
boolean showGroupBuyButton = ((isGroupbuy && !isSale && (price>0)) || groupBuyList.count)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345