如何替换if-Else

免责声明:本文并不肯定或者否定哪一种写法,仅仅为大家提供一些其他的编码思路或者一些值得借鉴的点子

if(condition){
    do stuff
} else (otherCondition) {
    do something
} else {

}

if-Else通常是个糟糕的选择,它导致设计复杂,代码可读性差,重构困难等等。说了这么多如何优化?或者说如何替换if-Else呢?

五种方式

完全不必要的Else块

public void PerformOperation(int input) {
    if(intput > 5) {
        // do something
    } else {
        // do something
    } 
}

只需要删除else块即可简化此过程,如下所示

public void PerformOperation(int input) {
    if(intput > 5) {
        // do something
        return;
    } 

    // do something
}

仔细体会一下,那种写法更容易理解?其实就一句话:在满足特定条件的情况下执行某些操作并立即返回,适用于异常流先返回,往下执行的都是正常流业务。

价值分配

如果你想要根据某些输入为变量分配新值,如下:

public static string determineGender(int input) {
    String gender = String.Empty;
    if(input == 0) {
        gender = "male";
    } else if(input == 1) {
        gender = "woman";
    } else if {
        gender = "unknown";
    }

    return gender;
}

上述if-Else很容易被开关取代。如下:

public static string determineGender(int input) {
    if(input == 0) {
         return "male";
    } else if(input == 1) {
         return "woman";
    } else if {
         return "unknown";
    }
}

进一步优化,可通过删除else来进一步简化代码

public static string determineGender(int input) {
    if(input == 0) {return "male"; } 
    if(input == 1) {return "woman"; }  

    return "unknown";
}

若不使用else,则我们将剩下干净的可读代码。这么做的好处是可以迅速得到想要的值。试想:如果已经找到正确的值,继续测试下一个值一点意义也没有。

前提条件检查(用三元运算代替if)

通常,我发现,如果方法提供了无效的值,则继续执行是没有意义的。假设有一个方法defineGender方法,要求输入值必须始终为0或者1。

// Input must be 0 or 1
public String defineGender (int input) {
    // Continue executing logic
}

在没有参数验证的情况下,执行该方法没有任何意义,或者说太容易出bug了,因此在实际业务之前,我们需要检查一些先决条件。

// Input must be 0 or 1
public String defineGender (int input) {
    if(intput < 0) throw new ArgumentException();
    if(intput > 0) throw new ArgumentException();

    return input == 0 ? "woman" : "man";
}

因有明确的输入限制,if可以用三元代替,因此不再需要在结尾处写默认返回值。

将 If-Else转换为字典,完全避免 IF-ELSE

假设你需要执行一些操作,这些操作将根据某些条件进行选择,我们知道以后必须添加更多条件操作。

public void performOp(String operationName) {
    if(operationName == "Op1")) {
       // something 
    } else if (operationName == "Op2") {
       // something 
    } else {
       // default path 
    }
}

有些人倾向于使用久经考验的 If-Else。如果添加新操作,则只需要简单地添加其他内容即可。很简单!但是,就维护而言,这种方法不是一个号的设计。

知道我们以后需要添加新的操作后,我们可以将If-Else重构为字典。

public void performOp(String operationName) {
    var operations = new Dictionary<String, Action>();
    operations["Op1"] = () => {// something};
    operations["Op2"] = () => {// something};

    operations[operationName].Invoke();
}

可读性已经大大提高,并且可以轻松地推断出该代码。注意,仅用于说明目的将字典放置在方法内部。你可以在其他地方定义它。

扩展应用程序,完全避免使用If-Else

public String printOrder(Order order, String formatType) {
    String result = String.Empty;

    if(formatType == "Json") {
        result = JsonSerializer.Serialize(order);
    } else if (formatType == "PlainText") {
        result = $"Id:{order.Id}\nSum: {order.Sum}";
    } else {
        result = "Unknown format";
    }

    return result;
}

上述代码我们若有新业务增加,则可通过添加if-else来解决,但它违反了开闭原则,且可读性差。

正确的做法为:遵循SOLID原则,我们通过实施动态类型发现过程(本例中为策略模式)来做到这一点。

重构的过程如下

  • 使用公共接口将每个分支提取到单独的策略类中。
  • 动态查找实现通用接口的所有类。
  • 根据输入决定执行哪种策略。

重点是类型发现的工作原理。

private String printOrder(Order order, String formatType) {

// Dynamic type discovery process that builds a dictionary
Dictionary<String, Type> formatterTypes = Assembly
    .GetExecutingAssembly()
    .GetExportedTypes()
    .Where(type => type.getInterfaces().Contains(typeof(IOrderOutputStrategy)))
    .ToDictionary(type => type.GetCustomAttribute<OutputFormatterName>().DisplayName);

Type choseFormatter = formatterTypes[formatType];

// Try instantiate the formatter -- could have utilized a DI framework here instead
IOrderOutputStrategy strategy = Activator.CreateInstance(chosenFormatter) as IOrderOutputStrategy;
if ( strategy is null) throw new InvalidOperationException("No valid formatter selected");

// Execute strategy method
string result = strategy.ConvertOrderToString(order);
return result;
}
读了有收获就请肥宅喝瓶怡宝吧!