Как избежать дублирования кода? - PullRequest
5 голосов
/ 27 января 2012

Можно ли избежать дублирования кода в таких случаях?(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
}

Возможным решением может быть использование Исключений, но это не очень хорошая практика.Существует ли так называемая хорошая схема управления потоком программ в таких случаях?Например, способ вызова break ; из функции check().(Возможно на других языках программирования)

Ответы [ 6 ]

4 голосов
/ 27 января 2012

Несколько хороших ответов (особенно сейчас у Гарретта) на сложный вопрос, но я добавлю свои $ 0,02 для потомков.

Здесь нет простого ответа о том, как реорганизовать этот блок, не видя фактический кодно моя реакция на это заключается в том, что он должен быть переработан.

Например, способ вызова break;изнутри функции check ().(Возможно, на других языках программирования)

Если вы запрашиваете другой разрыв, который Java не поддерживает (без взлома), и у вас есть дублированный check() и различные различные коды выхода / повторения цикла, это указывает надля меня это большой и сложный метод.Вот несколько идей для размышления:

  • Каждый из блоков some code here что-то делает.Если вы примените их к своим собственным методам, как это изменит цикл?

  • Может быть, разбить цикл на серию комментариев.Не углубляйтесь в код, но подумайте об этом концептуально, чтобы увидеть, выпадает ли другая конфигурация.

  • Был ли у вас другой разработчик в вашей организации, который не связан с этим кодом?взгляд на это?Если вы подробно объясните, как работает код кому-то, они могут увидеть некоторые шаблоны, которыми вы не являетесь, поскольку вы в сорняках.

Я также думаю, что идея @ 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;
        ...
    }
 }

Надеюсь, что это поможет и удачи.

2 голосов
/ 27 января 2012

Лучший способ избежать этого дублирования - не допустить, чтобы это произошло в первую очередь, оставив ваши методы небольшими и сфокусированными.

Если блоки // some code here не являются независимыми, вам необходимо опубликовать всекод, прежде чем кто-то может помочь вам реорганизовать его.Если они независимы, то есть способы его рефакторинга.

1 голос
/ 27 января 2012

Код запаха

Прежде всего, я отвечу второму Эксу: перепиши свой код! Для этого может помочь шаблон проектирования состояния . Я бы также сказал, что использование 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 <= 7 вам нужно будет использовать интерфейс с методом invoke () вместо этого, с 4 реализациями от codeBlock1 до codeBlock4. Конечно, этот подход совсем не читабелен, но настолько общий, что он будет работать для любой бизнес-логики внутри codeBlocks и даже для любого break / return / continue-orgy. </p>

0 голосов
/ 27 января 2012

Возможно, вы захотите подумать о переформулировании вашей логики как конечного автомата. Это может упростить вещи и, вероятно, облегчит логику.

0 голосов
/ 27 января 2012

Один из лучших способов сделать это - использовать операторы 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;


}
0 голосов
/ 27 января 2012

Не совсем. Второе продолжение избыточно (ваш код будет продолжен в любом случае). Попробуйте использовать оператор Switch . Это сделает ваш код более читабельным.

...