Funciones de una línea que se llaman solo una vez

116

Considere una función sin parámetros ( editar: no necesariamente) que realiza una sola línea de código, y se llama solo una vez en el programa (aunque no es imposible que sea necesario nuevamente en el futuro).

Podría realizar una consulta, verificar algunos valores, hacer algo que involucre expresiones regulares ... cualquier cosa oscura o "hacky".

La razón detrás de esto sería evitar evaluaciones difíciles de leer:

if (getCondition()) {
    // do stuff
}

donde getCondition() es la función de una línea.

Mi pregunta es simple: ¿es esta una buena práctica? Me parece bien, pero no sé a largo plazo ...

    
pregunta vemv 12.09.2011 - 16:34
fuente

11 respuestas

237

Depende de esa única línea. Si la línea es legible y concisa por sí misma, es posible que la función no sea necesaria. Ejemplo simplista:

void printNewLine() {
  System.out.println();
}

OTOH, si la función le da un buen nombre a una línea de código que contiene, por ejemplo, Una expresión compleja y difícil de leer, está perfectamente justificada (para mí). Ejemplo elaborado (dividido en varias líneas para facilitar la lectura aquí):

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}
    
respondido por el Péter Török 12.09.2011 - 16:36
fuente
66

Sí, esto se puede utilizar para satisfacer las mejores prácticas. Por ejemplo, es mejor hacer que una función con un nombre claro haga algo de trabajo, incluso si solo tiene una línea, que tener esa línea de código dentro de una función más grande y necesite un comentario de una línea que explique qué hace. Además, las líneas de código vecinas deben realizar tareas en el mismo nivel de abstracción. Un contraejemplo sería algo así como

startIgnition();
petrolFlag |= 0x006A;
engageChoke();

En este caso, definitivamente es mejor mover la línea central a una función con un nombre sensato.

    
respondido por el Kilian Foth 12.09.2011 - 16:39
fuente
36

Creo que, en muchos casos, esta función es un buen estilo, pero puede considerar una variable booleana local como alternativa en los casos en que no necesite usar esta condición en algún otro lugar, por ejemplo:

bool someConditionSatisfied = [complex expression];

Esto le dará una pista al lector de códigos y guardará la introducción de una nueva función.

    
respondido por el cybevnm 12.09.2011 - 17:15
fuente
23

Además de La respuesta de Peter , si es necesario que esa condición se actualice en algún momento en el futuro, al encapsularla de la forma que sugiere, solo tendría un único punto de edición.

Siguiendo el ejemplo de Peter, si esto

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isFemale() 
        && (taxPayer.getNumberOfChildren() > 2 
        || (taxPayer.getAge() > 50 && taxPayer.getEmployer().isNonProfit()));
}

se convierte en esto

boolean isTaxPayerEligibleForTaxRefund() {
  return taxPayer.isMutant() 
        && (taxPayer.getNumberOfThumbs() > 2 
        || (taxPayer.getAge() > 123 && taxPayer.getEmployer().isXMan()));
}

Realizas una sola edición y se actualiza universalmente. Mantenibilidad sabia, esto es un plus.

En cuanto al rendimiento, la mayoría de los compiladores de optimización eliminarán la llamada a la función y alinearán el pequeño bloque de código de todos modos. La optimización de algo como esto puede reducir el tamaño del bloque (eliminando las instrucciones necesarias para la llamada a la función, devolución, etc.), por lo que normalmente es seguro hacerlo incluso en condiciones que de otro modo podrían evitar la inclusión.

    
respondido por el Stephen 12.09.2011 - 17:13
fuente
5

Además de la legibilidad (o en su complemento), esto permite escribir funciones en el nivel adecuado de abstracción.

    
respondido por el tylermac 12.09.2011 - 20:00
fuente
4

Depende. A veces es mejor encapsular una expresión en una función / método, incluso si es solo una línea. Si es complicado de leer o si lo necesita en varios lugares, considero que es una buena práctica. A largo plazo, es más fácil de mantener, ya que ha introducido un punto de cambio único y una mejor legibilidad .

Sin embargo, a veces es solo algo que no necesitas. Cuando la expresión es fácil de leer de todos modos y / o simplemente aparece en un lugar, no la envuelvas.

    
respondido por el Falcon 12.09.2011 - 16:38
fuente
3

Creo que si solo tienes algunos de ellos, entonces está bien, pero el problema surge cuando hay muchos en tu código. Y cuando el compilador se ejecuta o el interpitor (según el idioma que utilice) va a esa función en la memoria. Entonces, digamos que tienes 3 de ellos. No creo que la computadora se dé cuenta, pero si empiezas a tener cientos de esas pequeñas cosas, entonces el sistema tiene que registrar funciones en la memoria que solo se llaman una vez y luego no se destruyen.

    
respondido por el WojonsTech 13.09.2011 - 07:36
fuente
3

He hecho esto exactamente recientemente en una aplicación que he estado refactorizando, para hacer explícito el significado real del código sin comentarios:

protected void PaymentButton_Click(object sender, EventArgs e)
    Func<bool> HaveError = () => lblCreditCardError.Text == string.Empty && lblDisclaimer.Text == string.Empty;

    CheckInputs();

    if(HaveError())
        return;

    ...
}
    
respondido por el joshperry 12.09.2011 - 20:33
fuente
0

Mover esa línea en un método bien nombrado hace que tu código sea más fácil de leer. Muchos otros ya han mencionado eso ("código de auto-documentación"). La otra ventaja de pasarlo a un método es que facilita la prueba unitaria. Cuando se aísla en su propio método y la unidad se prueba, puede estar seguro de que si se encuentra un error, no estará en este método.

    
respondido por el Andrew 16.09.2011 - 06:15
fuente
0

Ya hay muchas respuestas buenas, pero hay un caso especial que vale la pena mencionar .

Si su declaración de una línea necesita un comentario y usted puede identificar claramente (lo que significa: nombre) su propósito, considere extraer una función mientras mejora el comentario en el documento API. De esta manera usted hace que la llamada de la función sea más rápida y fácil de entender.

Es interesante que se puede hacer lo mismo si actualmente no hay nada que hacer, sino un comentario que recuerda las expansiones necesarias (en un futuro muy cercano 1) ), así que,

def sophisticatedHello():
    # todo set up
    say("hello")
    # todo tear down

también podría haber cambiado en esto

def sophisticatedHello():
    setUp()
    say("hello")
    tearDown()

1) debe estar realmente seguro de esto (consulte YAGNI )

    
respondido por el Wolf 09.01.2015 - 17:08
fuente
0

Si el idioma lo admite, normalmente uso funciones anónimas etiquetadas para lograr esto.

someCondition = lambda p: True if [complicated expression involving p] else False
#I explicitly write the function with a ternary to make it clear this is a a predicate
if (someCondition(p)):
    #do stuff...

En mi humilde opinión, este es un buen compromiso porque le brinda la facilidad de lectura de no tener la expresión complicada que abarrota la condición if mientras evita la saturación del espacio de nombres global / paquete con pequeñas etiquetas desechables. Tiene la ventaja añadida de que la función "definición" está justo donde se usa, lo que facilita su modificación y lectura.

No tiene que ser solo funciones de predicado. También me gusta encerrar repetidas calderas en pequeñas funciones como esta (funciona particularmente bien para la generación de listas pythonic sin saturar la sintaxis del corchete). Por ejemplo, el siguiente ejemplo simplificado cuando se trabaja con PIL en python

#goal - I have a list of PIL Image objects and I want them all as grayscale (uint8) numpy arrays
im_2_arr = lambda im: array(im.convert('L')) 
arr_list = [im_2_arr(image) for image in image_list]
    
respondido por el crasic 13.09.2011 - 02:43
fuente

Lea otras preguntas en las etiquetas