http://tommwq.tech/blog/2020/11/09/192
嵌套层次过深会导致代码难以理解和修改,也容易出现逻辑分支缺失,引入缺陷。导致嵌套层次过深的原因主要有两点:一是不恰当的嵌套,二是逻辑过于复杂。针对这两种情况,可以分别采用提前返回和封装的手法处理。
1 提前返回
一些代码在使用逻辑判断、循环和异常捕获时,嵌套层次不合理,导致嵌套层次过深。这种情况通常表现为,某个分支中的代码非常简短。这时可以采用提前返回的方式,压缩嵌套层级。
Listing 1: 嵌套不合理
public static Error checkError(List<DataTable<String, String>> result) { Error error = new Error(); if (result.size() > 0) { Iterator<Map<String, String>> it = result.get(0).iterator(); if (it.hasNext()) { Map<String, String> e = it.next(); if (e.containsKey("code")) { String code = e.get("code"); if ("0".equals(code)) { return null; } else { error.setCode(Integer.parseInt(code.replace("0x", ""), 16)); error.setMessage(e.get("message")); return error; } } else { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 1"); return error; } } else { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 2"); return error; } } else { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 3"); return error; } }
Listing 2: 提前返回
public static Error checkSmsError(List<DataTable<String, String>> result) { Error error = new Error(); if (result.isEmpty()) { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 1"); return error; } Iterator<Map<String, String>> it = result.get(0).iterator(); if (!it.hasNext()) { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 2"); return error; } Map<String, String> e = it.next(); if (!e.containsKey("code")) { error.setCode(Error.NET_WORK_ERR.errorCode); error.setMessage("ERROR 3"); return error; } String code = e.get("code"); if ("0".equals(code)) { return null; } String message = e.getOrDefault("message", ""); if (HelperUtil.shouldPreventSmsError(code, message)) { return null; } error.setCode(Integer.parseInt(code.replace("0x", ""), 16)); error.setMessage(message); return error; }
提前返回还有一种do-while(0)模式:
Listing 3: 嵌套不合理
FILE *f1 = fopen("a.txt", "r"); if (f1 != NULL) { FILE *f2 = fopen("b.txt", "r"); if (f2 != NULL) { FILE *f3 = fopen("b.txt", "r"); if (f3 != NULL) { int result = do_something(f1, f2, f3); fclose(f1); fclose(f2); fclose(f3); return result; } else { fclose(f1); fclose(f2); return -1; } } else { fclose(f1); return -1; } } else { return -1; }
Listing 4: 提前返回 do-while(0)
int result = 0; FILE *f1 = NULL; FILE *f2 = NULL; FILE *f3 = NULL; do { FILE *f1 = fopen("a.txt", "r"); if (f1 == NULL) { break; } FILE *f2 = fopen("b.txt", "r"); if (f2 == NULL) { break; } FILE *f3 = fopen("c.txt", "r"); if (f3 == NULL) { break; } result = do_something(f1, f2, f3); } while (0); if (f1 != NULL) { fclose(f1); } if (f2 != NULL) { fclose(f1); } if (f3 != NULL) { fclose(f1); } return result;
2 封装
除了嵌套不合理外,逻辑复杂也是导致嵌套层次过深的主要原因。对于这种情况可以采用封装的方法处理。
Listing 5: 逻辑复杂
double discountRate = 1.0; int extraDiscount = 0; if (itemId == Item.FRUIT) { if (itemCount >= 10) { discountRate = 0.9; } else if (itemCount >= 50) { discountRate = 0.85; extraDiscount = 10; } else if (itemCount >= 100) { discountRate = 0.8; extraDiscount = 20; } } else if (itemId == Item.MILK) { if (itemCount >= 20) { discountRate = 0.88; } } else { if (itemCount >= 30) { discountRate = 0.9; } } double originalPrice = itemCount * itemPrice; double price = originalPrice * discountRate - extraDiscount;
Listing 6: 封装
interface DiscountStrategy { double getDiscountedPrice(int itemCount, double originalPrice); } class NormalDiscountStrategy implements DiscountStrategy { public double getDiscountedPrice(int itemCount, double originalPrice) { double rate = 1.0; if (itemCount > 30) { rate = 0.9; } return originalPrice * rate; } } class MilkDiscountStrategy implements DiscountStrategy { public double getDiscountedPrice(int itemCount, double originalPrice) { double rate = 1.0; if (itemCount > 20) { rate = 0.88; } return originalPrice * rate; } } class FruitDiscountStrategy implements DiscountStrategy { public double getDiscountedPrice(int itemCount, double originalPrice) { double rate = 1.0; int extra = 0; if (itemCount >= 10) { rate = 0.9; } else if (itemCount >= 50) { rate = 0.85; extra = 10; } else if (itemCount >= 100) { rate = 0.8; extra = 20; } return originalPrice * rate - extra; } } class DiscountStrategyFactory { DiscountStrategy getDiscountStrategy(int itemId) { switch (itemId) { case Item.FRUIT: return new FruitDiscountStrategy(); case Item.MILK: return new MilkDiscountStrategy(); default: return new NormalDiscountStrategy(); } } } double originalPrice = itemCount * itemPrice; double price = DiscountStrategyFactory.getDiscountStrategy(itemId).getDiscountedPrice(itemCount, originalPrice);
采用封装的方法,总的代码行数会增加。但是每个模块(类、方法)的内聚更高,可复用性更好,客户端代码的编写变得更简单。