Pregunta

A menudo he escrito este tipo de función en ambos formatos, y me preguntaba si se prefiere un formato sobre otro y por qué.

public void SomeFunction(bool someCondition)
{
    if (someCondition)
    {
        // Do Something
    }
}

o

public void SomeFunction(bool someCondition)
{
    if (!someCondition)
        return;

    // Do Something
}

Por lo general, codifico con el primero, ya que esa es la forma en que funciona mi cerebro mientras codifica, aunque creo que prefiero el segundo, ya que se encarga de cualquier error de manejo de inmediato y me resulta más fácil leer

¿Fue útil?

Solución

Prefiero el segundo estilo. Primero obtenga casos no válidos, ya sea simplemente salir o elevar excepciones según corresponda, colocar una línea en blanco allí, luego agregar el cuerpo "real" del método. Me resulta más fácil leer.

Otros consejos

Definitivamente lo último. El primero no se ve mal en este momento, pero cuando obtienes un código más complejo, no puedo imaginar que nadie piense esto:

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    int retval = SUCCESS;
    if (someCondition)
    {
        if (name != null && name != "")
        {
            if (value != 0)
            {
                if (perms.allow(name)
                {
                    // Do Something
                }
                else
                {
                    reval = PERM_DENY;
                }
            }
            else
            {
                retval = BAD_VALUE;
            }
        }
        else
        {
            retval = BAD_NAME;
        }
    }
    else
    {
        retval = BAD_COND;
    }
    return retval;
}

es más legible que

public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
    if (!someCondition)
        return BAD_COND;

    if (name == null || name == "")
        return BAD_NAME;

    if (value == 0)
        return BAD_VALUE;

    if (!perms.allow(name))
        return PERM_DENY;

    // Do something
    return SUCCESS;
}

Admito completamente que nunca entendí la ventaja de los puntos de salida individuales.

Eso depende - En general, no voy a hacer todo lo posible para tratar de mover un montón de código para salir de la función temprano: el compilador generalmente se encargará de eso por mí. Dicho esto, sin embargo, si hay algunos parámetros básicos en la parte superior que necesito y que no puedo continuar de otra manera, romperé temprano. Del mismo modo, si una condición genera un gigante if Bloquear en la función Lo haré que se rompa temprano como resultado de eso también.

Dicho esto, sin embargo, si una función requiere algunos datos cuando se llama, generalmente voy a lanzar una excepción (ver ejemplo) en lugar de simplemente regresar.

public int myFunction(string parameterOne, string parameterTwo) {
  // Can't work without a value
  if (string.IsNullOrEmpty(parameterOne)) {
    throw new ArgumentNullException("parameterOne");
  } 
  if (string.IsNullOrEmpty(parameterTwo)) {
    throw new ArgumentNullException("parameterTwo");
  }

  // ...      
  // Do some work
  // ...

  return value;
}

Prefiero el regreso temprano.

Si tiene un punto de entrada y un punto de salida, siempre debe rastrear todo el código en su cabeza hasta el punto de salida (nunca se sabe si algún otro código de código hace algo más al resultado, por lo que usted tengo que rastrearlo hasta que exista). Usted hace eso, sin materia, qué rama determina el resultado final. Esto es difícil de seguir.

Con una entrada y múltiples, regresa cuando tiene su resultado y no se moleste en rastrearlo hasta ver que nadie le hace nada más (porque no habrá nada más desde que regresó). Es como hacer que el cuerpo del método se divida en más pasos, que cada paso con la posibilidad de devolver el resultado o dejar que el siguiente paso intente su suerte.

En la programación C donde tienes que limpiar manualmente, hay mucho que decir para el retorno de un punto. Incluso si no hay necesidad en este momento de limpiar algo, alguien podría editar su función, asignar algo y necesitar limpiarla antes de regresar. Si eso sucede, será un trabajo de pesadilla revisando todas las declaraciones de devolución.

En la programación C ++ tienes destructores e incluso ahora ahora guardias de alcance. Todos estos deben estar aquí para asegurarse de que el código sea seguro de excepción en primer lugar, por lo que el código está bien protegido contra la salida temprana y, por lo tanto, no tiene un inconveniente lógico y es puramente un problema de estilo.

No tengo bastante conocimiento sobre Java, si se llamará al código de bloque "finalmente" y si los finalizadores pueden manejar la situación de necesidad de garantizar que algo suceda.

C# ciertamente no puedo responder.

Da-Sanguage le brinda guardias de salida de alcance incorporados y, por lo tanto, está bien preparado para la salida temprana y, por lo tanto, no debe presentar un problema que no sea el estilo.

Las funciones, por supuesto, no deberían ser tan largas en primer lugar, y si tiene una gran declaración de conmutación, su código probablemente también se tenga en cuenta.

Devoluciones tempranas para la ganancia. Pueden parecer feos, pero mucho menos feos que grandes if envoltorios, especialmente si hay múltiples condiciones para verificar.

Yo uso ambos.

Si DoSomething Es 3-5 líneas de código, entonces el código se ve hermoso usando el primer método de formato.

Pero si tiene muchas más líneas que eso, prefiero el segundo formato. No me gusta cuando los soportes de apertura y cierre no están en la misma pantalla.

Una razón clásica para la exitosa ejecución de una sola entrada es que de lo contrario la semántica formal se vuelve indescriptiblemente fea de lo contrario (la misma razón por la que Goto se consideró perjudicial).

Dicho de otra manera, es más fácil razonar cuándo su software saldrá de la rutina si solo tiene 1 devolución. Que también es un argumento contra las excepciones.

Por lo general, minimizo el enfoque de retorno temprano.

Personalmente, prefiero hacer controles de condición de aprobación/fallas al principio. Eso me permite agrupar la mayoría de las fallas más comunes en la parte superior de la función con el resto de la lógica a seguir.

Eso depende.

Retorno temprano Si hay alguna condición obvia de Deat End para verificar de inmediato, lo que haría que ejecutar el resto de la función sin sentido.*

Establezca RETVAL + retorno único si la función es más compleja y podría tener múltiples puntos de salida de lo contrario (problema de legibilidad).

*Esto a menudo puede indicar un problema de diseño. Si encuentra que muchos de sus métodos deben verificar algún estado externo/paramater o tal antes de ejecutar el resto del código, eso es probablemente algo que la persona que llama debe manejar.

Utilice un IF

En el libro de Don Knuth sobre GoTo's I Le Lo leo, dar una razón para tener siempre la condición más probable que sea lo primero en una declaración IF. Bajo el supuesto de que esta sigue siendo una idea razonable (y no una de pura consideración para la velocidad de la época). Diría que los retornos tempranos no son buenas prácticas de programación, especialmente teniendo en cuenta el hecho de que la mayoría de las veces no se usan para el manejo de errores, a menos que su código tenga más probabilidades de fallar de lo que no falla :-)

Si sigue los consejos anteriores, necesitaría poner esa retorno en la parte inferior de la función, y entonces también podría ni siquiera llamarlo una devolución allí, simplemente establecer el código de error y devolverlo dos líneas por lo tanto. Logrando así el ideal de salida 1 de salida 1.

Delphi específico ...

Tengo la mente de que esta es una buena práctica de programación para los programadores de Delphi, aunque no tengo ninguna prueba. Antes de D2009, no tenemos una forma atómica de devolver un valor, tenemos exit; y result := foo; O podríamos simplemente arrojar excepciones.

Si tuviera que sustituir

if (true) {
 return foo;
} 

por

if true then 
begin
  result := foo; 
  exit; 
end;

Es posible que se enferme de ver eso en la parte superior de cada una de sus funciones y prefiera

if false then 
begin
  result := bar;

   ... 
end
else
   result := foo;

Y solo evita exit en total.

Estoy de acuerdo con la siguiente declaración:

Personalmente, soy un fanático de las cláusulas de guardia (el segundo ejemplo), ya que reduce la sangría de la función. A algunas personas no les gustan porque resulta en múltiples puntos de retorno de la función, pero creo que es más claro con ellos.

Tomado de Esta pregunta en StackOverflow.

Utilizo devoluciones tempranas casi exclusivamente en estos días, hasta extremo. Escribo esto

self = [super init];

if (self != nil)
{
    // your code here
}

return self;

como

self = [super init];
if (!self)
    return;

// your code here

return self;

Pero realmente no importa. Si tiene más de uno o dos niveles de anidación en sus funciones, deben ser reventados.

Preferiría escribir:

if(someCondition)
{
    SomeFunction();
}

Al igual que tú, generalmente escribo el primero, pero prefiero el último. Si tengo muchos cheques anidados, generalmente refactoran al segundo método.

No me gusta cómo se aleja el manejo de errores del cheque.

if not error A
  if not error B
    if not error C
      // do something
    else handle error C
  else handle error B
else handle error A

Prefiero esto:

if error A
  handle error A; return
if error B
  handle error B; return
if error C
  handle error C; return

// do something

Las condiciones en la parte superior se llaman "condiciones previas". Poniendo if(!precond) return;, está enumerando visualmente todas las condiciones previas.

Usar el gran bloque "if-else" puede aumentar la sobrecarga de sangría (olvidé la cita sobre las sangrías de 3 niveles).

Prefiero mantener las declaraciones pequeñas.

Entonces, elegir entre:

if condition:
   line1
   line2
    ...
   line-n

y

if not condition: return

line1
line2
 ...
line-n

Elegiría lo que describiste como "retorno temprano".

Eso sí, no me importan las devoluciones tempranas o lo que sea, realmente me gusta simplificar el código, acortar los cuerpos de las declaraciones IF, etc.

Anidados if's y for's y while son horrible, evítelos a toda costa.

Como otros dicen, depende. Para pequeñas funciones que devuelvan los valores, puedo codificar las devoluciones tempranas. Pero para las funciones considerables, me gusta siempre tener un lugar en el código donde sé que puedo poner algo que se ejecutará antes de que regrese.

Practico Fail Fast en el nivel de función. Mantiene el código consistente y limpio (para mí y para aquellos con los que he trabajado). Por eso siempre regreso temprano.

Para algunas condiciones verificadas, puede implementar aspectos para esas comprobaciones si usa AOP.

Licenciado bajo: CC-BY-SA con atribución
scroll top