¿Es esto un abuso de try / finally?


Dado que las declaraciones de retorno múltiples son aceptables (estoy en cierto modo en desacuerdo, pero divaguemos ), estoy buscando una manera más aceptable de lograr el siguiente comportamiento:

Opción A : devoluciones múltiples, bloque de código repetido

public bool myMethod() {
    /* ... code ... */

    if(thisCondition) {
        /* ... code that must run at end of method ... */
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        /* ... the SAME code that must run at end of method ... */
        return false;
    }

    /* ... even more code ... */

    /* ... the SAME CODE AGAIN that must run at end of method ... */
    return lastCondition;
}

Me hace sentir sucio ver el mismo (pequeño) bloque de código repetido tres veces cada vez que el método regresa. Además, me gustaría aclarar que las dos return false declaraciones anteriores sin duda pueden describirse como volviendo método medio... no son absolutamente " declaraciones de la guardia."

Es la Opción B ligeramente más aceptable? Siento que puedo abusar de intentar / finalmente, y espero que haya algo completamente diferente que yo debería estar haciendo.

Option B : multiple returns, try / finally block (without catch blocks / exceptions)

public bool myMethod() {
    try {
        /* ... code ... */

        if(thisCondition) {
            return false;
        }

        /* ... more code ... */

        if(thatCondition) {
            return false;
        }

        /* ... even more code ... */

        return lastCondition;
    } finally {
        /* ... code that must run at end of method ... */
    }
}

Finalmente, la opción C es la mejor solución en mi libro, pero a mi equipo no le gusta este enfoque para cualquiera que sea la razón (s), por lo tanto estoy buscando un compromiso.

Opción C : retorno único, bloques condicionales

public bool myMethod() {
    /* ... code ... */

    if(!thisCondition) {
        /* ... more code ... */
    }

    if(!thisCondition && !thatCondition) {
        /* ... even more code ... */
    }

    /* ... code that must run at end of method ... */
    return summaryCondition;
}

Si desea discutir varias declaraciones de retorno, por favor hágalo en esta pregunta.

Author: Community, 2010-02-09

11 answers

Las excepciones deben ser excepcionales, así que no me gusta la opción B si no hay otras excepciones alrededor(Nota para los downvoters - No digo que tener finalmente es incorrecto solo que prefiero no tenerlo si no hay excepciones - si tiene razones, por favor comente)

Si siempre se necesita código, ¿qué tal refactorizar en 2 funciones

public bool myMethod() {
    bool summaryCondition = myMethodWork();
    // do common code
    return summaryCondition;
}

private bool myMethodWork() {
   /* ... code ... */

    if(thisCondition) {
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        return false;
    }

    /* ... even more code ... */

    return lastCondition;
}
 28
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-07 11:44:44

Si el código necesita ejecutarse incluso cuando cualquier otro código arroja una excepción, entonces el bloque finally es la solución correcta.

Si no necesita ejecutarse en el caso de una excepción (es decir, solo es necesario para retornos "normales"), entonces usar finally sería abusar de la característica.

Personalmente reescribiría ese método en el estilo de punto de retorno único. No porque me adhiera religiosamente a esa idea (no lo hago), sino porque es la más adecuada para este tipo de fin de método códigos.

Cuando ese código resulta ser demasiado complicado (y esa es una posibilidad muy real), entonces es hora de refactorizar el método extrayendo uno o más métodos.

La refactorización más simple sería algo como esto:

public boolean  myMethod() {
    boolean result = myExtractedMethod();
    /* ... code that must run at end of method ... */
    return result;
}

protected boolean myExtractedMethod() {
    /* ... code ... */

    if(thisCondition) {
        return false;
    }

    /* ... more code ... */

    if(thatCondition) {
        return false;
    }

    /* ... even more code ... */
    return lastCondition;
}
 29
Author: Joachim Sauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 16:55:58

Este es un lugar perfecto para un GOTO

* patos *

 15
Author: David Oneill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 17:00:10

Su opción C solución no está lejos de ser óptima, ya que codifica adecuadamente la secuencia de ejecución adecuada que está tratando de lograr.

Del mismo modo, usando sentencias if anidadas hacen lo mismo. Puede ser visualmente menos atractivo, pero es más simple de entender, y hace que el flujo de ejecución sea bastante obvio:

public bool myMethod() { 
    boolean  rc = lastCondition; 

    /* ... code-1 ... */ 

    if (thisCondition) { 
        rc = false;
    } 
    else {  
        /* ... code-2 ... */ 

        if (thatCondition) { 
            rc = false;
        } 
        else {  
            /* ... code-3 ... */ 
            rc = ???;
        }  
    }

    /* ... the code that must run at end of method ... */ 
    return rc;  
}

Simplificando el código produce:

public bool myMethod() { 
    boolean  rc = false; 

    /* ... code-1 ... */ 

    if (!thisCondition) { 
        /* ... code-2 ... */ 

        if (!thatCondition) { 
            /* ... code-3 ... */ 
            rc = lastCondition;
        }  
    }

    /* ... the code that must run at end of method ... */ 
    return rc;  
}

El código simplificado también revela lo que realmente estás tratando de lograr: usando las condiciones de prueba para evitarejecutar código, por lo tanto probablemente debería estar ejecutando ese código cuando las condiciones son falseen lugar de hacer algo cuando son true.

Para responder a tu pregunta sobre intenta-finalmente bloques: Sí, puedes abusar de ellos. Su ejemplo no es lo suficientemente complejo como para justificar el uso de try-finally. Si fuera más complejo, podría ser.

Vea mi opinión en: Ir A Declaración Considerada Perjudicial: Retrospectiva , "Manejo de Excepciones".

 3
Author: David R Tribble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 17:57:50

Si el código necesita ejecutarse incluso cuando hay un Exception, entonces finally no es solo una buena opción, es una necesidad. Si ese no es el caso, finally no es necesario. Parece que quieres encontrar el formato que "se ve" mejor. Pero hay poco más en juego aquí.

 3
Author: fastcodejava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-20 09:11:46

¿Qué tal dividirlo un poco más para dar algo más ( excusando mi no haber usado los operadores lógicos de Java en bastante tiempo ) de esta manera:

public bool findFirstCondition()
{
   // do some stuff giving the return value of the original "thisCondition".
}

public bool findSecondCondition()
{
   // do some stuff giving the return value of the original "thatCondition".
}

public bool findLastCondition()
{
   // do some stuff giving the return value of the original "lastCondition".
}

private void cleanUp() 
{
   // perform common cleanup tasks.
}


public bool myMethod() 
{ 


   bool returnval = true;
   returnval = returnval && findFirstCondition();
   returnval = returnval && findSecondCondition();

   returnval = returnval && findLastCondition();
   cleanUp();
   return returnval; 
}
 2
Author: glenatron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 17:07:48

No abuses de try/finally a menos que necesites salir de los bucles internos. Abuso hacer / mientras.

bool result = false;
do {
  // Code
  if (condition1) break;
  // Code
  if (condition2) break;
  // . . .
  result = lastCondition
} while (false);
 2
Author: Rex Kerr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 17:33:57

Usar try/finally para controlar el flujo me parece como usar GOTO.

 0
Author: cherouvim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 16:52:18

¿Hay alguna razón por la que no puedes simplemente almacenar el valor devuelto y salir del if?

   bool retVal = true;
   if (retVal && thisCondition) {
   }

   /* more code */

   if ( retVal ) {
     /* ... code that must run at end of method, maybe inside an if or maybe not... */

   }
   return retVal;
 0
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 16:57:38

IMO la idea es poner un bloque try sobre una pequeña parte del código (por ejemplo, una llamada a un método) que puede lanzar una excepción conocida (por ejemplo, leer desde un archivo, leer un int a un String). Así que poner un bloque try sobre todo el código de un método no es realmente el camino a seguir, a menos que realmente esté esperando que todos y cada código de condición if potencialmente arrojen el mismo conjunto de excepciones. No veo un punto en usar un bloque try solo por el bien de usar un finally.

Si no me equivoco, poner grandes trozos de código dentro de un try también hace que sea mucho más lento, pero no estoy seguro de si esto es cierto en las últimas versiones de Java.

Personalmente, iría con la Opción C. Pero tampoco tengo nada en contra de la Opción A.

 0
Author: MAK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 16:59:23

A menos que el código que debe ejecutarse al final del método use variables locales del método, podría extraerlo en un método como:

public boolean myMethod() {
    /* ... code ... */

    if(thisCondition) {
        return myMethodCleanup(false);
    }

    /* ... more code ... */

    if(thatCondition) {
        return myMethodCleanup(false);
    }

    /* ... even more code ... */

    return myMethodCleanup(lastCondition);
}

private boolean myMethodCleanup(boolean result) {

    /* ... the CODE that must run at end of method ... */
    return result;
}

Esto todavía no se ve muy bien, pero es mejor que usar construcciones similares a goto. Para convencer a tus compañeros de equipo de que una solución de 1 retorno podría no ser tan mala, también puedes presentar una versión usando 2 do { ... } while (false); y break's (*evil grin*.)

 0
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 17:06:40