【问题标题】:How to avoid code duplication?如何避免代码重复?
【发布时间】:2012-01-27 12:26:07
【问题描述】:

在这种情况下是否可以避免代码重复? (Java代码)

void f()
{
    int r;
    boolean condition = true;
    while(condition)
    {
        // some code here (1)

        r = check();
        if(r == 0)
            break ;
        else if(r == 1)
            return ;
        else if(r == 2)
            continue ;
        else if(r == 3)
            condition = false;

        // some code here (2)

        r = check();
        if(r == 0)
            break ;
        else if(r == 1)
            return ;
        else if(r == 2)
            continue ;
        else if(r == 3)
            condition = false;

        // some code here (3)
    }
    // some code here (4)
}

int check()
{
    // check a condition and return something
}

一个可能的解决方案可能是使用异常,但这似乎不是一个好的做法。 在这种情况下,有没有所谓的程序流控制的好模式?例如,一种从check() 函数内部调用break ; 的方法。 (可能在其他编程语言中)

【问题讨论】:

  • 也许你可以创建一个递归函数,并在 r 等于 3 时回调一次。
  • 退货和破案有什么区别?在while循环之后你有一些代码吗?
  • @ShaMan-H_Fel:是的,我愿意。但这对问题没有太大影响。
  • @Pf94 虽然有些人喜欢递归函数,但我不建议他们替换上面示例中的简单代码。使用递归时,调试变得非常讨厌。

标签: java design-patterns code-duplication control-flow


【解决方案1】:

一些很好的答案(尤其是@Garrett 刚刚回答的)一个棘手的问题,但我会为后代添加我的 0.02 美元。

对于如何在不查看实际代码的情况下重构此块,这里没有简单的答案,但我对此的反应是它需要重新设计。

例如,一种调用 break 的方法;从 check() 函数内部。 (可能在其他编程语言中)

如果您要求 Java 不支持的不同中断(没有 hack)并且重复的 check() 和各种不同的循环退出/重复代码向我表明这是一个大而复杂的方法。这里有一些想法供您考虑:

  • 每个some code here 块都在做某事。如果你把它们拉到他们自己的方法中,那会如何改变循环?

  • 可能会将循环分解为一系列 cmets。不要深入研究代码,而是从概念上考虑它,看看是否有不同的配置丢失。

  • 您的组织中是否有其他未参与此代码的开发人员来查看它?如果你详细解释代码是如何工作的,他们可能会看到一些你没有的模式,因为你在杂草中。

我也认为@aix 的有限状态机想法是一个不错的想法,但我在编程过程中很少需要使用这种机制——主要是在模式识别期间。我怀疑将更小的代码块放入方法中重新设计代码将足以改进代码。

如果您确实想在此处实现状态机,请查看更多详细信息。你可能有一个循环,它只运行一个调用方法的 switch 语句。每个方法都会返回开关的下一个值。这与您的代码不完全匹配,但类似于:

int state = 0;
WHILE: while(true) {
    switch (state) {
       case 0:
            // 1st some code here
            state = 1;
            break;
       case 1:
            state = check();
            break;
       case 2:
            return;
       case 3:
            break WHILE;
       case 4:
            // 2nd some code
            state = 1;
            break;
        ...
    }
 }

希望这能有所帮助,祝你好运。

【讨论】:

  • +1 以获得全面的答案。虽然我怀疑当你看到他所做的只是控制流(中断、返回、继续)时,基于状态的逻辑可以完全重构。
  • 通过开关的状态机概念写得很好。请注意,在案例 4 中,在“第二部分代码”之后切换回 state 1 并不等同于问题中的代码,因为在随后的 while 循环中,需要执行“第三部分代码”。因此,无论您是在第一种还是第二种情况区分块中,您都需要以某种方式编码到状态中。使用一个额外的布尔变量,它只在必要时被检查,而不添加更多的 switch-case,对我来说是最好的。
  • 同意。它不应该是完美的。重点是展示设计,但实际上是鼓励重新设计代码。
【解决方案2】:

避免这种重复的最好方法是一开始就不要让它发生,方法是保持你的方法小而专注。

如果// some code here 块不是独立的,那么您需要发布所有代码,然后有人可以帮助您重构它。如果它们是独立的,那么就有办法对其进行重构。

【讨论】:

    【解决方案3】:

    代码味道

    首先,我第二个aix的回答:重写你的代码!为此,state design pattern 可能会有所帮助。我还要说,以这种方式使用 break、continue 和 return 与代码重复本身一样有代码味道。

    话虽如此,这里有一个解决方案,只是为了好玩

    private int r;
    void f()
    {
        distinction({void => codeBlock1()}, {void => codeBlock4()}, {void => f()}, 
          {void => distinction( {void => codeBlock2()},{void => codeBlock4()},
                                {void => f()}, {void => codeBlock3()} )
          });
    }
    
    void distinction( {void=>void} startingBlock, {void=>void} r0Block, {void=>void} r2Block, {void=>void} r3Block){ 
            startingBlock.invoke();
            r = check();
            if(r == 0)
                r0Block.invoke();
            else if(r == 1)
                {}
            else if(r == 2)
                r2Block.invoke(); 
            else if(r == 3)
                // if condition might be changed in some codeBlock, you still
                // would need the variable condition and set it to false here.
                r3Block.invoke();
    }
    

    这使用了闭包。当然,参数 r0Block 和 r2Block 可以省略,取而代之的是 codeBlock4() 和 f() 硬编码在区分() 中。但是区分()只能由 f()使用。对于 Java

    【讨论】:

      【解决方案4】:

      不是真的。 第二个 continue 是多余的(您的代码无论如何都会继续)。 尝试使用Switch 语句。这将使您的代码更具可读性。

      【讨论】:

      • 这是我实际代码的简化形式。我现在将对其进行编辑以使其更加真实。
      【解决方案5】:

      一个更好的方法是使用 switch 语句,如下所示:

      void f()
      {
      int r;
      boolean condition = true;
      while(condition)
      {
      outerloop:
      
      
          r = check();
          switch(r){
      
          case 0: break outerloop;
      
          case 1: return;
      
          case 2: continue;
      
          case 3: condition  = false;
      
      
      }
      

      【讨论】:

      【解决方案6】:

      您可能想考虑将您的逻辑重新表述为状态机。它可能会简化事情,并且可能会使逻辑更容易理解。

      【讨论】: