前言
前面我们分别讲解了Java编程的命名与注释、代码风格和编程技巧这些Java基础教程,今天我们继续讲解解Java程序员必备的技巧篇。
1. 函数设计要职责单一
我们在前面讲到单一职责原则的时候,针对的是类、模块这样的应用对象。实际上,对于函数的设计来说,更要满足单一职责原则。相对于类和模块,函数的粒度比较小,代码行数少,所以在应用单一职责原则的时候,没有像应用到类或者模块那样模棱两可,能多单一就多单一。
具体的代码示例如下所示:
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);
2. 移除过深的嵌套层次
代码嵌套层次过深往往是因为 if-else、switch-case、for 循环过度嵌套导致的。我个人建议,嵌套最好不超过两层,超过两层之后就要思考一下是否可以减少嵌套。过深的嵌套本身理解起来就比较费劲,除此之外,嵌套过深很容易因为代码多次缩进,导致嵌套内部的语句超过一行的长度而折成两行,影响代码的整洁。
解决嵌套过深的方法也比较成熟,有下面 4 种常见的思路。
去掉多余的 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;
}
除此之外,常用的还有通过使用多态来替代 if-else、switch-case 条件判断的方法。这个思路涉及代码结构的改动,我们会在后面的章节中讲到,这里就暂时不展开说明了。
3. 学会使用解释性变量
常用的用解释性变量来提高代码的可读性的情况有下面 2 种。
常量取代魔法数字。示例代码如下所示:
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 {
// ...
}