如何避免代码重复?

5
在这种情况下,是否有可能避免代码重复?(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时调用它回来。 - Kevin
返回和中断(break)情况有什么区别?while循环后面是否有一些代码? - ShaMan-H_Fel
@ShaMan-H_Fel:是的,我知道。但这并不影响问题的答案。 - semekh
@Pf94 虽然有些人喜欢递归函数,但我不建议用它们来替换像上面例子中那样简单明了的代码。使用递归时调试变得非常棘手。 - Hidde
6个回答

4

这是一个关于如何重构这个代码块的问题,没有实际的代码很难给出简单的答案,但我的反应是需要重新设计。

例如,从check()函数中调用break;(可能在其他编程语言中)

如果你想要一个Java不支持的不同的break(没有hack),并且有重复的check()和各种不同的循环退出/重复代码,这表明这是一个庞大而复杂的方法。以下是一些可以考虑的想法:

  • 每个some code here块都在做某些事情。如果将它们分别放到自己的方法中,那么循环会发生什么变化?

  • 也许把循环拆分成一系列注释。不要深入代码,但从概念上思考,看看是否可以得出不同的配置。

  • 您的组织中是否有另一个与此代码无关的开发人员来查看它?如果您详细解释了代码的工作原理,他们可能会发现您没有注意到的一些模式,因为您正在进行具体操作。

我还认为@aix的有限状态机的想法是好的,但我在编程过程中很少需要使用这种机制,主要是在模式识别过程中。我认为重构代码,并将较小的代码块放入方法中,就足以改进代码。

如果你确实想要在这里实现状态机,以下是一些更多的细节。您可以有一个只运行单个switch语句并调用方法的循环。每个方法都将返回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;
        ...
    }
 }

希望以下内容能够帮到您,祝您好运。


2
尽管我怀疑基于状态的逻辑可以完全重构,当您看到他在状态中所做的只是控制流程(break、return、continue)时。 - Garrett Hall
状态机概念通过switch很好地写下来了。请注意,在第4种情况中的“2nd some code”之后切换回state 1并不等同于问题中的代码,因为在随后的while循环中,需要执行“3rd some code”。因此,您需要以某种方式将状态编码为您是否在第一个或第二个情况区分块中。在必要时仅检查的附加布尔变量,而不添加其他switch-case,对我来说看起来最好。 - DaveFar
同意。它本来就不应该是完美的。重点是展示设计,但实际上是为了鼓励重新设计代码。 - Gray

2

避免函数重复的最好方法是从一开始就保持你的方法小而专注。

如果// some code here块不是独立的,那么您需要在请求重构之前发布所有代码。 如果它们是独立的,则可以进行重构。


1

代码异味

首先,我赞同aix的答案:重写你的代码!为此,状态设计模式可能会有所帮助。我还想说,在这种情况下使用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 可以省略,而是在 distinction() 中硬编码 codeBlock4() 和 f()。但这样,distinction() 只能被 f() 使用。在 Java <=7 中,您需要使用具有 invoke() 方法的接口,并使用 4 个实现 codeBlock1 到 codeBlock4。当然,这种方法不太可读,但是通用性强,可以适用于代码块中的任何业务逻辑,甚至适用于任何 break/return/continue-orgy。


0

更好的方法是使用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;


}

请查看此教程:http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html - DukeOfMarmalade
所以,这与我预期的不同。// some code here在哪里? - semekh

0

不是真的。 第二个continue是多余的(你的代码会继续执行)。 尝试使用Switch语句。它会使你的代码更易读。


那只是我实际代码的简化版本。我现在会编辑它,使其更加实际。 - semekh

0
你可能想要考虑将你的逻辑重构为状态机。这样做可能会简化事情,并且很可能会使逻辑更易于理解跟踪。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接