【问题标题】:Refactoring a complicated if-condition重构复杂的 if 条件
【发布时间】:2011-02-15 20:52:33
【问题描述】:

任何人都可以建议最好的方法来避免大多数情况吗?我有以下代码,如果有条件,我想避免大多数情况,该怎么做?任何解决方案都有很大帮助;

if (adjustment.adjustmentAccount.isIncrease) {
    if (adjustment.increaseVATLine) {
        if (adjustment.vatItem.isSalesType) {
            entry2.setDebit(adjustment.total);
            entry2.setCredit(0d);
        } else {
            entry2.setCredit(adjustment.total);
            entry2.setDebit(0d);
        }
    } else {
        if (adjustment.vatItem.isSalesType) {
            entry2.setCredit(adjustment.total);
            entry2.setDebit(0d);
        } else {
            entry2.setDebit(adjustment.total);
            entry2.setCredit(0d);
        }
    }
} else {
    if (adjustment.increaseVATLine) {
        if (adjustment.vatItem.isSalesType) {
            entry2.setCredit(adjustment.total);
            entry2.setDebit(0d);
        } else {
            entry2.setDebit(adjustment.total);
            entry2.setCredit(0d);
        }
    } else {
        if (adjustment.vatItem.isSalesType) {
            entry2.setDebit(adjustment.total);
            entry2.setCredit(0d);
        } else {
            entry2.setCredit(adjustment.total);
            entry2.setDebit(0d);
        }
    }
}

【问题讨论】:

    标签: java refactoring conditional if-statement


    【解决方案1】:

    我认为这行得通。我基本上概括了你的布尔逻辑。下次,试着画一些图表来帮助你理清思路。

    编辑:我想从 cmets 到这篇文章中指出,Marcelo 和 BlueRaja 提供的 XOR 解决方案在功能上是相同的。

    /* This is to avoid a crazy 3-way switch. Generalised.
     * Instead of using a complicated if-else branch, we can use the number of true
     * and false to entail the intended action. */
    /* This is the same as a ^ b ^ c (chained XOR), 
     * which is used to count the parity of truth values. */
    int a = adjustment.adjustmentAccount.isIncrease ? 1 : 0;
    int b = adjustment.increaseVATLine ? 1 : 0;
    int c = adjustment.vatItem.isSalesType ? 1 : 0;
    
    if ((a + b + c) % 2 == 1)
    {
        entry2.setDebit(adjustment.total);          // Odd number of trues
        entry2.setCredit(0d);
    }
    else
    {
        entry2.setCredit(adjustment.total);         // Even number of trues
        entry2.setDebit(0d);
    }
    

    【讨论】:

    • 感谢 Xavier Ho,您的代码提供了帮助。你有什么建议在大多数情况下避免 if 条件
    • 我建议您也阅读其他人的答案,因为他们的答案比我的答案更加普遍,我的答案是为了解决您的特殊问题。试着理解布尔逻辑是如何工作的,看看你能做些什么来简化它们。 =]
    • 智能但高度不可读的解决方案。这意味着难以编辑、维护和理解。我会避免编写那种“聪明”的代码。
    • 如果你仔细想想,它真的没有比这里介绍的其他解决方案的一半“更糟糕”的地方。虽然您的解决方案采用了完全不同的方法,但我认为我的解决方案更健壮,更便携/可修改。如果我是 OP,我还将提供原始代码(已注释掉)以澄清所概括的内容,每个案例旁边都有一个数字。我把它们放在我的编辑器上,这就是我找到这个解决方案的原因。
    • @XavierHo: (T^T)^F = F^F = F. (F^F)^T = F^T = T. (F^F)^F = F^F = F。因此,实际上,实际的 XOR 和 Outcome 列是完全相同的。使用 XOR 会给出正确的结果。附带说明一下,在基本的电气工程(和密码学!)课程中,您使用 XOR 门来计算字节的奇偶校验,这正是我们的两个解决方案所做的。
    【解决方案2】:

    问题已得到解答,但我会在此处为那些关心更清洁解决方案的人发帖:

    //Set debit if exactly one or all three are true, else set credit
    if(adjustment.adjustmentAccount.isIncrease ^ adjustment.increaseVATLine ^
       adjustment.vatItem.isSalesType)
    {
        entry2.setDebit(adjustment.total);
        entry2.setCredit(0d);
    }
    else
    {
        entry2.setCredit(adjustment.total);
        entry2.setDebit(0d);
    }
    

    【讨论】:

    • “清洁工”显然在旁观者的眼中。 :)
    【解决方案3】:

    最好的解决方案是遵循设计模式。 基于状态的设计模式为每个状态定义一个类。

    然后状态类封装该特定状态的操作过程。 这不仅可以防止出现大量 if -else 语句网格。

    【讨论】:

    • 它通过将所有的 if/else 逻辑分散到多个类中来避免将所有的 if/else 逻辑放在一个地方!这不是我书中的奖励。 (如有疑问,请避免遵循设计模式。)
    【解决方案4】:

    怎么办...让我们提取几个方法,这样我们可以更好地看到逻辑。

    private void a() {
        entry2.setDebit(adjustment.total);
        entry2.setCredit(0d);
    }
    private void b() {
        entry2.setCredit(adjustment.total);
        entry2.setDebit(0d);
    }
    
    if (adjustment.adjustmentAccount.isIncrease) {
        if (adjustment.increaseVATLine) {
            if (adjustment.vatItem.isSalesType) {
                a();
            } else {
                b();
            }
        } else {
            if (adjustment.vatItem.isSalesType) {
                b();
            } else {
                a();
            }
        }
    } else {
        if (adjustment.increaseVATLine) {
            if (adjustment.vatItem.isSalesType) {
                b();
            } else {
                a();
        }
    } else {
        if (adjustment.vatItem.isSalesType) {
            a();
        } else {
            b();
        }
    }
    

    现在,看看它,第一个块

    if (adjustment.increaseVATLine) {
        if (adjustment.vatItem.isSalesType) {
            a();
        } else {
            b();
        }
    } else {
        if (adjustment.vatItem.isSalesType) {
            b();
        } else {
            a();
        }
    }
    

    如果adjustment.increaseVATLineadjustment.vatItem.isSalesType 具有相同的值,则相当于执行a(),否则为b()。所以我们可以减少它:

    if (adjustment.adjustmentAccount.isIncrease) {
        if (adjustment.increaseVATLine == adjustment.vatItem.isSalesType) {
            a();
        } else {
            b();
        }
    } else {
        if (adjustment.increaseVATLine) {
            if (adjustment.vatItem.isSalesType) {
                b();
            } else {
                a();
            }
        } else {
            if (adjustment.vatItem.isSalesType) {
                a();
            } else {
                b();
            }
        }
    }
    

    而剩下的块也是一样的,只是把a()b()颠倒过来:

    if (adjustment.adjustmentAccount.isIncrease) {
        if (adjustment.increaseVATLine == adjustment.vatItem.isSalesType) {
            a();
        } else {
            b();
        }
    } else {
        if (adjustment.increaseVATLine == adjustment.vatItem.isSalesType) {
            b();
        } else {
            a();
        }
    }
    

    所以我们开始看到逻辑。如果是增加,并且 increaseVATLine 与 isSalesType 匹配,则我们借记,否则贷记,但如果是减少,则仅当它们不匹配时才贷记。有什么好的表达方式?好吧,首先,更聪明地命名 a() 和 b() - 现在我们可以看到它们在做什么

    if (adjustment.adjustmentAccount.isIncrease) {
        if (adjustment.increaseVATLine == adjustment.vatItem.isSalesType) {
            debitEntry();
        } else {
            creditEntry();
        }
    } else {
        if (adjustment.increaseVATLine == adjustment.vatItem.isSalesType) {
            creditEntry();
        } else {
            debitEntry();
        }
    }
    

    现在它更加清晰了。当帐户是增加帐户和增加增值税行和销售类型时,或当它是减少并且它是减少增值税行或它是销售类型时,借记帐户,但不是两者兼而有之。这个真值表有帮助吗?第一列是adjustmentAmount.isIncrease;第二个是adjustment.increaseVATLine;第三个是adjustment.vatItem.isSalesType。第四列是 D 表示借方,C 表示贷方;括号中是标志中 TRUE 值的数量。

    TTT -> D (3) 
    TFF -> D (1) 
    TTF -> C (2)
    TFT -> C (2) 
    FTT -> C (2) 
    FFF -> C (0)
    FTF -> D (1) 
    FFT -> D (1)
    

    现在您可以了解@Xavier Ho 的解决方案为何有效;奇数的都是借方,偶数的都是贷方。

    这只是一个探索路径;我希望它会有所帮助。

    【讨论】:

    • 我很高兴有人真正弄清楚了我的解决方案为何有效。 +1。但是我现在觉得 XOR 解决方案要好得多。 =]。
    【解决方案5】:

    我还没有彻底验证逻辑,但这是基本思想:

    amt = adjustment.total
    if (adjustment.adjustmentAccount.isIncrease
        ^ adjustment.increaseVATLine
        ^ adjustment.vatItem.isSalesType)
    {
        amt = -amt;
    }
    
    entry2.setCredit(amt > 0 ? amt : 0);
    entry2.setDebit(amt < 0 ? -amt : 0);
    

    我应该注意到,这个逻辑略有不同,因为它正确处理了adjustment.total 的负值,而原始逻辑似乎假设(也许正确)该值总是非负的。

    【讨论】:

    • 感谢 Marcelo Cantos,您的代码提供了帮助。你有什么建议在大多数情况下避免 if 条件
    • 如果我能给你一个食谱,我们就不需要程序员了。一般来说,这只是实践和经验的问题。卡诺图可以为这个过程带来一定程度的形式化,但我个人发现更容易以代数方式操作逻辑(例如,方程!(expr1 &amp;&amp; expr2) = !expr1 || !expr2 可以简化问题,具体取决于表达式的性质)。此外,与上述情况一样,寻找冗余并尝试将其删除;再说一次,我知道这没有正式的过程,只是实践和经验。
    【解决方案6】:

    对于马丁·史密斯的评论,我会补充:

    请记住,Karnaugh 可以帮助您简化 if 的条件。

    【讨论】:

    • 啊哈,K-map!让我回到大学一年级的美好时光。 +1
    • 电气工程师解决方案的方法! :-)
    【解决方案7】:

    你可以使用这样的真值表:

    debit = ((isIncrease && increaseVATLine && !isSalesType) ||
             (isIncrease && !increaseVATLine && isSalesType) ||
             (!isIncrease && increaseVATLine && isSalesType) ||
             (!isIncrease && !increaseVATLine && !isSalesType)) ? 0 : adjustment.total;
    entry2.setCredit(debit);
    

    没有任何 ifs,您可以很容易地看到在哪些情况下借方为 0。贷方也是如此。

    【讨论】:

      【解决方案8】:

      通常可以在一定程度上缓解这种情况的方法是使用继承。

      例如,如果您有两个类 IncreaseNonIncrease 是同一个超类的子类,那么您可以拥有一个方法 doSomething,它可以根据您当前拥有的任何类来做 - 很好 - 一些事情。然后,您不必检查“如果对象是做 X”,只需调用 .doSomething(),它就会做它应该做的任何事情。

      然后你可以更进一步,拥有越来越多的子类来进一步“优化”和“避免更多的 if”。

      可能还有其他可能性(很大程度上取决于您的环境/要求),例如函数指针、委托、strategy pattern (GoF) 或此类构造。

      【讨论】:

      • 过度设计解决方案的方法!
      • 哦,是的,这让我想起了:stackoverflow.com/questions/2101875/…
      • 嗯 - @BlueRaja:这对我来说似乎有点粗鲁。如果我没记错的话,这是我第一次提到 SO 上的设计模式。只是因为我不够聪明,无法在没有更聪明的人帮助的情况下编写干净的代码。也许我最好不要听从像 Erich Gamma 这样平庸的程序员的建议。
      • 我同意对于 OPs 案例,清理逻辑似乎可以解决问题。我想我陷入了问答与论坛的陷阱(即回答确切的问题与讨论问题)。只是出于好奇:从 OP 问题(“avoid most if conditions”)的表述来看,你怎么能确定她/他没有在更广泛的意义上思考?这就是我写“usually can do”而不是“answer to your exact question is”的原因...
      【解决方案9】:

      您似乎只有 2 个案例,因此您可以将它们与 OR AND 等结合起来。

              if (<case1expression>) {
                  entry2.setCredit(adjustment.total);
                  entry2.setDebit(0d);
              } else {
                  entry2.setDebit(adjustment.total);
                  entry2.setCredit(0d);
              }
      

      【讨论】:

        【解决方案10】:

        如果您有条件逻辑(例如,如果满足条件就做某事),您为什么还要尝试避免它们?

        【讨论】:

        • 因为有一个更简单的解决方案。
        猜你喜欢
        • 1970-01-01
        • 2019-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-05
        • 2016-08-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多