4.2 快速改善代码质量的编程规范
4.2.1 命名
命名的一个原则就是以能准确达意为目标
4.2.1.1 多长合适
- 作用域大的用更达意的长命名,如类名;反之用短命名
4.2.1.2 利用上下文
如下代码:不应在成员变量的命名中重复添加“user”这样一个前缀单词,而应直接命名为 name、password、avatarUrl
public class User {
private String userName;
private String userPassword;
private String userAvatarUrl;
//...
}
4.2.1.3 命名要可读、可搜索
-
可读,指的是不要用一些特别生僻、难发音的英文单词来命名。如:plateaux, eyrie
-
可搜索:经常会用“关键词联想”的方法来自动补全和搜索。比如,键入某个对象“.get”,希望 IDE 返回这个对象的所有 get 开头的方法。
统一规约很重要,都用“selectXXX”表示查询,就不要用“queryXXX”;都用“insertXXX”表示插入一条数据,要不用“addXXX”
4.2.1.4 如何命名接口和抽象类
- 接口命名:两种比较常见的方式:
- 加前缀“I”,表示一个 Interface。比如 IUserService;
- 不加前缀,实现类加Impl。如UserService,实现类UserServiceImpl
- 抽象类命名:加前缀“Abstract”,如AbstractUserService
4.2.2 注释
4.2.2.1 该写什么?
- 做什么、为什么、怎么做
/**
* (what) Bean factory to create beans.
*
* (why) The class likes Spring IOC framework, but is more lightweight.
*
* (how) Create objects from different sources sequentially:
* user specified object > SPI > configuration > default object.
*/
public class BeansFactory {
// ...
}
4.2.2.2 注释要多吗?
- 类和函数一定要写注释,而且要写得尽可能全面、详细;
- 函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码的可读性。
4.2.3 代码风格
- [x] 类和函数不要超过一个显示屏的垂直高度;
- [x] 一行代码最长不能超过 IDE 显示的宽度;
- [x] 善用空行分割单元块
- [x] 两格缩进节省空间,不要用TAB(tab与空格混用的情况下在不同IDE会显示不一致)
- [x] 大括号是否要另起一行?不用
- [x] 类中成员的排列顺序:
- 成员变量排在函数的前面;
- 成员变量之间或函数之间,按照“先静态、后普通”的方式来排列;
- 成员变量之间或函数之间,还会按照作用域范围从大到小的顺序来排列,先 public ,再 protected ,最后 private 。
4.2.4 实用编程技巧
4.2.4.1 代码分隔成更小的代码块
-
阅读代码的习惯都是,先看整体再看细节。所以,要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节,让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。
// 重构前的代码 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; }
4.2.4.2 避免函数参数过多
-
考虑函数是否职责单一,是否能通过拆分成多个函数的方式来减少参数
public User getUser(String username, String telephone, String email); // 拆分成多个函数 public User getUserByUsername(String username); public User getUserByTelephone(String telephone); public User getUserByEmail(String email);
-
将函数的参数封装成对象
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);
4.2.4.3 勿用函数参数来控制逻辑
-
true 的时候走这块逻辑,false 的时候走另一块逻辑。违背了单一职责原则和接口隔离原则。将其拆成两个函数,可读性上也要更好
public void buyCourse(long userId, long courseId, boolean isVip); // 将其拆分成两个函数 public void buyCourse(long userId, long courseId); public void buyCourseForVip(long userId, long courseId);
4.2.4.4 函数设计要职责单一
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);
4.2.4.5 移除过深的嵌套层次
-
去掉多余的 if 或 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; } } // 示例二 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; }
-
使用编程语言提供的 continue、break、return 关键字,提前退出嵌套
// 重构前的代码 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; } // 重构后的代码:使用continue提前退出 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)) { continue; } matchedStrings.add(str); // 此处还有10行代码... } } 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) { if (str.contains(substr)) { matchedStrings.add(str); } } } } return matchedStrings; } // 重构后的代码:先执行判空逻辑,再执行正常逻辑 public List<String> matchStrings(List<String> strList,String substr) { if (strList == null || substr == null) { //先判空 return Collections.emptyList(); } List<String> matchedStrings = new ArrayList<>(); for (String str : strList) { if (str != null) { if (str.contains(substr)) { matchedStrings.add(str); } } } 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; }
4.2.4.6 使用解释性变量
-
常量取代魔法数字
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 { // ... }
4.2.4.7 善用编程语言提供的语法糖,如lambda表达式
https://www.cnblogs.com/franson-2016/p/5593080.html
list循环操作
String[] atp = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomas Berdych",
"Juan Martin Del Potro"};
List<String> players = Arrays.asList(atp);
// 以前的循环方式
for (String player : players) {
System.out.print(player + "; ");
}
// 使用 lambda 表达式以及函数操作(functional operation)
players.forEach((player) -> System.out.print(player + "; "));
// 在 Java 8 中使用双冒号操作符(double colon operator)
players.forEach(System.out::println);
实现Runnable接口
// 1.1使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 1.2使用 lambda expression
new Thread(() -> System.out.println("Hello world !")).start();
// 2.1使用匿名内部类
Runnable race1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
};
// 2.2使用 lambda expression
Runnable race2 = () -> System.out.println("Hello world !");
// 直接调用 run 方法(没开新线程哦!)
race1.run();
race2.run();
使用Lambdas排序集合
// 1.1 使用匿名内部类根据 name 排序 players
Arrays.sort(players, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return (s1.compareTo(s2));
}
});
// 1.2 使用 lambda expression 排序 players
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);
// 1.3 也可以采用如下形式:
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));