http://tommwq.tech/blog/2020/11/09/192

嵌套层次过深会导致代码难以理解和修改,也容易出现逻辑分支缺失,引入缺陷。导致嵌套层次过深的原因主要有两点:一是不恰当的嵌套,二是逻辑过于复杂。针对这两种情况,可以分别采用提前返回和封装的手法处理。

降低代码嵌套层数的两种方法-LMLPHP

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);

采用封装的方法,总的代码行数会增加。但是每个模块(类、方法)的内聚更高,可复用性更好,客户端代码的编写变得更简单。

03-15 15:45