2010-11-21 12 views
16

este código:Es un "if (...) return ...;" sin "otro" considerado buen estilo?

if(someCondition) 
    return doSomething(); 

return doSomethingElse(); 

frente a este código:

if(someCondition) 
    return doSomething(); 
else 
    return doSomethingElse(); 

En esencia, son lo mismo, pero lo que es mejor estilo/rendimiento/... (si hay alguna no obstinado parte de la respuesta, por supuesto)? También considere el caso con múltiples "if else's":

if(someCondition) 
    return doSomething(); 
else if(someOtherCondition) 
    return doSomethingDifferently(); 
//... 
else 
    return doSomethingElse(); 

¡Gracias!

+0

2º tiene una declaración else innecesaria, no se está probando ninguna condición así que ¿para qué usarla? – DumbCoder

+0

@DumbCoder: Un error tipográfico simple ... Lo he arreglado. –

+5

-1: Subjetivo y propenso a comenzar una guerra de llamas. –

Respuesta

35

Cuando tiene varias declaraciones de devolución en una función, esto se conoce como "devolución anticipada". Si haces un Google search para "devolución anticipada", encontrarás un enlace tras otro que dice que es malo.

Digo tonterías.

Hay dos razones principales y una razón secundaria por la que las personas afirman que el regreso temprano es malo. Los revisaré y daré mi refutación en orden. Tenga en cuenta que esta es toda mi opinión y, en última instancia, debe decidir por usted mismo.

1) Motivo: Las devoluciones anticipadas dificultan la limpieza.

Refutación: para eso es RAII. Un programa bien diseñado no asignará recursos de tal manera que si la ejecución abandona el alcance de forma anticipada, esos recursos se filtrarán. En vez de hacer esto:

...

int foo() 
{ 
    MyComplexDevice* my_device = new MyComplexDevice; 
    // ... 
    if(something_bad_hapened) 
    return 0; 
    // ... 
    delete my_device; 
    return 42; 
} 

hacer esto:

int foo() 
{ 
    std::auto_ptr<MyComplexDevice> my_device(new MyComplexDevice); 
    if(something_bad_hapened) 
    return 0; 
    // ... 
    return 42; 
} 

Y pronto retorno no causará una pérdida de recursos. En la mayoría de los casos, ni siquiera necesitará usar auto_ptr porque va a crear matrices o cadenas, en cuyo caso usará vector, string o algo similar.

Debe diseñar su código así de todos modos para mayor robustez, debido a la posibilidad de excepciones. Las excepciones son una forma de devolución anticipada, como una declaración explícita return, y debe estar preparado para manejarlas. No puede manejar la excepción en foo(), pero foo() no debe tener fugas independientemente.

2) Motivo: Las devoluciones anticipadas hacen que el código sea más complejo. Refutación: los retornos iniciales realmente hacen que el código sea más simple.

Es una filosofía común que las funciones deben tener una responsabilidad. Estoy de acuerdo con eso. Pero la gente lleva esto demasiado lejos y concluye que si una función tiene múltiples rendimientos, debe tener más de una responsabilidad. (Extienden esto diciendo que las funciones nunca deben tener más de 50 líneas de longitud, o algún otro número arbitrario). Digo que no. El hecho de que una función tenga solo una responsabilidad, no significa que no tenga mucho que hacer para cumplir con esa responsabilidad.

Tomemos como ejemplo la apertura de una base de datos. Esa es una responsabilidad, pero está compuesta de muchos pasos, cada uno de los cuales podría salir mal. Abra la conexión. Iniciar sesión. Obtener un objeto de conexión & devolverlo. 3 pasos, cada uno de los cuales podría fallar Se podría descomponerlo en 3 sub-pasos, pero entonces, en lugar de tener un código como éste:

int foo() 
{ 
    DatabaseObject db = OpenDatabase(...); 
} 

podrás llegar a tener:

int foo() 
{ 
    Connection conn = Connect(...); 
    bool login = Login(...); 
    DBObj db = GetDBObj(conn); 
} 

Así que ha movido la realidad sólo supuestas responsabilidades múltiples a un punto más alto en la pila de llamadas.

3) Motivo: Los puntos de retorno múltiples no están orientados a objetos. Refutación: Esta es solo otra forma de decir "todos dicen que los retornos múltiples son malos, aunque realmente no sé por qué".

Visto de otra manera, esto es realmente solo un intento de meter todo en una caja con forma de objeto, incluso cuando no pertenece allí. Claro, tal vez la conexión es un objeto. Pero es el inicio de sesión? Un intento de inicio de sesión no es (IMO) un objeto. Es una operación. O un algoritmo. tratar de tomar este algoritmo y meterlo en una caja con forma de objeto es un intento gratuito en OOP, y solo dará como resultado un código que es más complejo, más difícil de mantener y posiblemente incluso menos eficiente.

+2

+10 aunque solo me permite dar 1 – Tergiver

+0

Aunque estoy seguro de que esto puede verse como personal y ambiguo, creo que el esfuerzo solo (y por supuesto el hecho de que estoy de acuerdo con cada punto) merece la marca verde ! – rubenvb

3

Es puramente una cuestión de preferencia personal o normas de codificación. Personalmente, preferiría una tercera variante:

return someCondition ? doSomething() : doSomethingElse(); 
+4

If los dos tipos de retorno de función que son ambos convertibles al tipo de retorno del alcance envolvente, pero no son idénticos entre sí, el operador ternario no podrá compilar. Entonces necesitarás un elenco feo para arreglarlo. Estaría a favor del ternario aquí solo cuando la condición involucrada es trivial y es poco probable que los tipos cambien (por ejemplo, las funciones llamadas están en la biblioteca estándar, no en el código de la aplicación). –

-1

Depende.

Si está siguiendo una convención de devoluciones anticipadas para manejar casos de error, entonces eso es bueno, si solo está haciendo cosas arbitrarias, entonces es malo.

El problema más grave con el código presentado es que no está utilizando llaves. Eso significa que no entiendo muy bien de qué se trata la claridad. Si no fuera por eso, simplemente respondería a su pregunta principal con "apuntar a la claridad", pero primero debe comprender la claridad. Como primer paso en ese camino, comienza a usar llaves. Intente hacer que su código sea legible por otros, tanto que lo pueda entender de un vistazo alguien más (o usted mismo meses/año después).

Saludos & HTH.,

+1

Bueno, veo las llaves para una línea if-else como "confusa", y solo las uso cuando hay varias líneas en el bloque condicional, pero ese es un asunto personal y no es realmente relevante para mi pregunta ... – rubenvb

+4

-1: Odio cuando las personas se quejan de omitir llaves. Es una convención perfectamente razonable omitir las llaves si aplica sangría al código correctamente, y todo el mundo sabe ir y agregar llaves si agregan una segunda línea de código. –

+5

Personalmente, me parece agregar llaves sin ninguna razón para REDUCIR la claridad, ya que agrega ruido visual y disminuye la cantidad de código funcional que cabe en una pantalla. – swestrup

3

Depende, yo prefiero lo mismo que FredOverflow

return someCondition ? doSomething() : doSomethingElse(); 

Si esto es suficiente. Si no es así, me he preguntado por una situación similar: si tiene un código más largo, digamos 30-40 líneas o más, debería poner return; en un lugar, eso no es necesario. Por ejemplo, considere la siguiente situación:

if(cond1) 
{ 
    if(cond2) 
    { 
     // do stuff 
    } 
    else if(cond3) 
    { 
     // .. 
    } 
} 
else if(cond4) 
{ 
    // .. 
} 
else 
{ 
    //.. 
}

Lo que me preguntaba era - ¿Debo poner return; (en función void) al final de cada caso - se trata de un estilo bueno o malo codificación (como no lo hace importa si hay return; o no). Finalmente decidí ponerlo, porque el desarrollador, que leerá este código más tarde, para saber que este es un estado final y ya no hay nada que hacer en esta función. No leer el resto del código, si él/ella es interesante solo en este caso.

7

Una función siempre debe regresar tan pronto como sea posible. Esto ahorra gastos indirectos semánticos en declaraciones innecesarias. Las funciones que regresan lo antes posible ofrecen la mayor claridad y el código fuente más limpio y más fácil de mantener.

El código de estilo SESE era bueno cuando tenía que escribir manualmente para liberar todos los recursos que había asignado, y recordar liberarlos a todos en varios lugares diferentes era una pérdida. Ahora que tenemos RAII, sin embargo, definitivamente es redundante.

Cuestiones relacionadas