¿Puede una función ser demasiado corta?

125

Cada vez que me encuentro escribiendo la misma lógica más de una vez, generalmente la coloco en una función, así que solo tengo un lugar en la aplicación para mantener esa lógica. Un efecto secundario es que a veces termino con una o dos funciones de línea como:

function conditionMet(){
   return x == condition;
}

O

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}

¿Esto es perezoso o una mala práctica? Solo pregunto porque esto resulta en un mayor número de llamadas a funciones para muy pequeñas piezas de lógica.

    
pregunta Mark Brown 01.04.2011 - 21:34

19 respuestas

166

Jeje, oh, señor Brown, si tan solo pudiera persuadir a todos los desarrolladores que conozco para que mantengan sus funciones tan pequeñas como estas, créeme, ¡el mundo del software sería un lugar mejor!

1) La legibilidad de tu código aumenta diez veces.

2) Es tan fácil entender el proceso de su código debido a la legibilidad.

3) SECO: no te repitas, ¡te estás adaptando muy bien a esto!

4) Probable. Las funciones diminutas son un millón de veces más fáciles de probar que los métodos de 200 líneas que vemos con demasiada frecuencia.

Ah, y no te preocupes por el "salto de función" en términos de rendimiento. Las compilaciones "Release" y las optimizaciones del compilador se encargan de esto muy bien para nosotros, y el rendimiento es el 99% del tiempo en otra parte en el diseño de sistemas.

¿Esto es perezoso? - ¡Muy al contrario!

¿Es esta mala práctica? - Absolutamente no. Es mejor utilizar esta forma de hacer métodos que las bolas de alquitrán u "Objetos de Dios" que son muy comunes.

Sigan con el buen trabajo de mi compañero artesano;)

    
respondido por el Martin Blore 01.04.2011 - 21:27
63

Yo diría que un método refactorizado es demasiado corto si:

  • Duplica una operación primitiva, con el único propósito de convertirla en un método:

Ej:

boolean isNotValue() {
   return !isValue();
}

o ...

  • El código solo se usa una vez, y su intención es fácil de entender de un vistazo.

Ej:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}

Este podría ser un patrón válido, pero simplemente alinearía el método configureDialog (), en este ejemplo, a menos que tuviera la intención de anularlo o reutilizar este código en otra parte.

    
respondido por el RMorrisey 02.04.2011 - 03:49
59

¿Puede una función ser demasiado corta? En general no.

De hecho, la única forma de garantizar que:

  1. Has encontrado todas las clases en tu diseño
  2. Sus funciones están haciendo una sola cosa.

Es mantener tus funciones lo más pequeñas posible. O, en otras palabras, extraiga funciones de sus funciones hasta que no pueda extraer más. A esto lo llamo "Extraer hasta que caigas".

Para explicar esto: una función es un ámbito con porciones de funcionalidad que se comunican mediante variables. Una clase es también un ámbito con partes de funcionalidad que se comunican mediante variables. Por lo tanto, una función larga siempre se puede reemplazar por una o más clases con un método pequeño.

Además, una función que es lo suficientemente grande como para permitirte extraer otra función de ella, está haciendo más de una cosa por definición . Entonces, si puede extraer una función de otra, debería extraer esa función.

A algunas personas les preocupa que esto lleve a una proliferación de funciones. Tienen razon Va a. Eso es realmente una buena cosa. Es bueno porque las funciones tienen nombres. Si tiene cuidado de elegir buenos nombres, entonces estas funciones actúan como publicaciones firmes que dirigen a otras personas a través de su código. De hecho, las funciones bien nombradas dentro de clases bien nombradas dentro de espacios de nombres bien nombrados son una de las mejores maneras de asegurarse de que sus lectores NO se pierdan.

Hay mucho más sobre esto en el Episodio III de Clean Code en cleancoders.com

    
respondido por el Uncle Bob. 02.04.2011 - 01:25
51

Wow, la mayoría de estas respuestas no son muy útiles en absoluto.

No debe escribirse ninguna función cuya identidad sea su definición . Es decir, si el nombre de la función es simplemente el bloque de código de la función escrito en inglés, entonces no lo escriba como una función.

Considera tu función conditionMet y esta otra función, addOne (perdóname por mi JavaScript oxidado):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }

conditionMet es una definición conceptual adecuada; addOne es una tautology . conditionMet es bueno porque no sabes lo que conditionMet implica simplemente diciendo "condición cumplida", pero puedes ver por qué addOne es tonto si lo lees en inglés:

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!

¡Por amor a cualquier cosa que aún pueda ser sagrada, por favor, no escriba funciones tautológicas!

(Y por la misma razón, no escriba comentarios para cada línea de código !)

    
respondido por el Rei Miyasaka 12.04.2017 - 09:31
14

Diría que si crees que la intención de un código puede mejorarse agregando un comentario, en lugar de agregar ese comentario, extrae el código en su propio método. No importa cuán pequeño sea el código.

Por ejemplo, si su código se vería así:

if x == 1 { ... } // is pixel on?

haz que se vea así en su lugar:

if pixelOn() { ... }

con

function pixelOn() { return x == 1; }

O en otras palabras, no se trata de la longitud del método, sino del código de auto-documentación.

    
respondido por el Julio 07.02.2013 - 21:46
7

Creo que esto es exactamente lo que quieres hacer. En este momento, esa función podría ser solo una o dos líneas, pero con el tiempo podría crecer. Además, tener más llamadas de función le permite leer las llamadas de función y comprender lo que está sucediendo allí. Esto hace que su código sea muy DRY (No se repita) que es mucho más fácil de mantener.

    
respondido por el mpenrow 01.04.2011 - 22:07
5

Estoy de acuerdo con todas las otras publicaciones que he visto. Este es un buen estilo.

La sobrecarga de un método tan pequeño puede ser nula, ya que el optimizador puede optimizar la llamada y alinear el código. Un código simple como este permite al optimizador hacer su mejor trabajo.

El código debe escribirse para mayor claridad y simplicidad. Intento limitar un método a uno de dos roles: tomar decisiones; o realizar un trabajo. Esto puede generar una línea de métodos. Cuanto mejor estoy haciendo esto, mejor encuentro mi código.

Este código tiende a tener una alta cohesión y un bajo acoplamiento, lo que es una buena práctica de codificación.

EDITAR: Una nota sobre los nombres de los métodos. Use un nombre de método que indique lo que el método no hace como lo hace. Me parece que verb_noun (_modifier) es un buen esquema de denominación. Esto le da nombres como Find_Customer_ByName en lugar de Select_Customer_Using_NameIdx. El segundo caso es propenso a volverse incorrecto cuando se modifica el método. En el primer caso, puede intercambiar la implementación completa de la base de datos del Cliente.

    
respondido por el BillThor 09.06.2011 - 02:19
4

Refactorizar una línea de código en una función parece excesivo. Puede que haya casos excepcionales, como ver loooooong / comples lines o expessions, pero no lo haría a menos que sepa que la función crezca en el futuro.

y su primer ejemplo sugiere el uso de elementos globales (que pueden o no hablar de otros problemas en el código), lo refactorizaría aún más y establecería esas dos variables como parámetros:

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");

El ejemplo conditionMet podría ser útil si la condición era larga y repetitiva, como:

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}
    
respondido por el FrustratedWithFormsDesigner 01.04.2011 - 21:43
3

Considera esto:

Una función de detección de colisión simple:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}

Si escribieras ese "simple" una línea en tu código todo el tiempo, podrías cometer un error. Además, sería realmente tortuoso escribir eso una y otra vez.

    
respondido por el muntoo 02.04.2011 - 10:35
2

No, y eso rara vez es un problema. Ahora, si alguien siente que ninguna función debería ser más larga que una línea de código (si solo pudiera ser así de simple), eso sería un problema y de alguna manera perezoso porque no están pensando en lo que es apropiado.

    
respondido por el JeffO 01.04.2011 - 22:17
2

Yo diría que son demasiado cortos, pero esta es mi opinión subjetiva.

Porque:

  • No hay razón para crear una función si se usa solo una o dos veces. Saltando a defs chupar. Especialmente con el código VS y C ++ increíblemente rápido.
  • Resumen de la clase. Cuando tienes miles de funciones pequeñas, me enoja. Disfruto cuando puedo ver definiciones de clase y rápidamente ver lo que hace, no cómo SetXToOne, SetYToVector3, MultiplyNumbers, + 100 setters / getters.
  • En la mayoría de los proyectos, estos ayudantes se convierten en un peso muerto después de una o dos fases de refactorización, y luego "busca todo" - > elimina para eliminar el código obsoleto, generalmente ~ 25% + de él.

Las funciones que son largas son malas, pero las funciones que tienen menos de 3 líneas y que solo realizan 1 cosa son igualmente malas en mi humilde opinión.

Así que diría que solo escribe una función pequeña si es:

  • 3+ líneas de código
  • Hace cosas que los desarrolladores junior pueden perderse (no saber)
  • hace validación adicional
  • Se usa, o se usará al menos 3x
  • Simplifica la interfaz de uso frecuente
  • No se convertirá en un peso muerto durante la próxima refactorización
  • Tiene algún significado especial, por ejemplo, especialización de plantillas o algo así
  • Hace algún trabajo de aislamiento: las constantes referencias, afectan los parámetros mutables, la recuperación de miembros privados

Apuesto a que el próximo desarrollador (senior) tendrá mejores cosas que hacer que recordar todas las funciones de SetXToOne. Así que se convertirán en peso muerto muy pronto de cualquier manera.

    
respondido por el Coder 09.06.2011 - 15:49
1

No me gusta el ejemplo no. 1, debido a su nombre genérico.

¿

conditionMet no parece ser genérico, por lo que representa una condición específica? Me gusta

isAdult () = { 
  age >= 18 
}

Esto estaría bien. Es una diferencia semántica, mientras que

isAtLeast18 () { age >= 18; } 

no estaría bien para mí.

Tal vez se use con frecuencia y esté sujeto a cambios posteriores:

getPreferredIcecream () { return List ("banana", "choclate", "vanilla", "walnut") }

también está bien. Usándolo varias veces, solo necesitas cambiar un lugar, si es necesario, tal vez la crema batida sea posible mañana.

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }

no es atómico, y debería darle una buena oportunidad de prueba.

Si necesitas pasar una función, por supuesto, pasa lo que quieras y escribe funciones que parezcan tontas.

Pero en general, veo muchas más funciones, que son demasiado largas, que funciones que son demasiado cortas.

Una última palabra: algunas funciones solo parecen apropiadas, porque están escritas de forma muy detallada:

function lessThan (a, b) {
  if (a < b) return true else return false; 
}

Si ves, es lo mismo que

return (a < b); 

no tendrás problemas con

localLessThan = (a < b); 

en lugar de

localLessThan = lessThan (a, b); 
    
respondido por el user unknown 09.06.2011 - 03:51
0

No hay código como ningún código!

Manténgalo simple y no complique las cosas.

No es ser perezoso, ¡está haciendo tu trabajo!

    
respondido por el RDL 02.04.2011 - 07:06
0

Sí, está bien tener una función de código corto. En el caso de métodos como "getters", "setters", "accesors" es muy común, como se mencionó en las respuestas anteriores.

A veces, esas funciones cortas de "accesores" son virtuales, porque cuando se sobrescriben en subclases, las funciones tendrán más código.

Si desea que la función no sea tan breve, bueno, en muchas funciones, ya sean globales o de métodos, generalmente uso una variable "resultado" (estilo pascal) en lugar de un retorno directo, es muy útil cuando se utiliza un depurador.

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}
    
respondido por el umlcat 02.04.2011 - 21:40
0

Demasiado corto nunca es un problema. Algunas razones para funciones cortas son:

Reusabilidad

Por ejemplo, Si tiene una función, como un método de conjunto, puede afirmarlo para asegurarse de que los parámetros son válidos. Esta comprobación debe realizarse en cualquier lugar donde se establezca la variable.

Mantenimiento

Puede usar alguna declaración que piense que en el futuro puede cambiar. P.ej. ahora muestra un símbolo en una columna, pero luego eso podría cambiar a otra cosa (o incluso un pitido).

Uniformity

Estás utilizando, por ejemplo, El patrón de fachada y lo único que hace una función es exactamente pasar la función a otra sin cambiar los argumentos.

    
respondido por el Michel Keijzers 14.08.2012 - 16:47
0

Cuando le das un nombre a una sección de código, es esencialmente para darle un nombre . Esto puede ser por varias razones, pero el punto crucial es si puede darle un nombre significativo que se agregue a su programa. Los nombres como "addOneToCounter" no agregarían nada, pero conditionMet() sí.

Si necesita una regla para descubrir si el fragmento es demasiado corto o demasiado largo, considere cuánto tiempo le lleva encontrar un nombre significativo para el fragmento. Si no puede hacerlo dentro de un período de tiempo razonable, el fragmento no tiene el tamaño adecuado.

    
respondido por el user1249 14.08.2012 - 17:33
0

No, pero puede ser demasiado conciso.

Recuerde: el código se escribe una vez, pero se lee muchas veces.

No escriba código para el compilador. Escríbalo para los futuros desarrolladores que deberán mantener su código.

    
respondido por el Jim G. 08.02.2013 - 01:55
-1

Sí, cariño, la función podría ser cada vez más pequeña y si es buena o mala, depende del idioma / marco que estés usando.

En mi opinión, la mayoría de las veces trabajo en tecnologías frontales, las funciones pequeñas se usan principalmente como funciones de ayuda, por lo que las utilizaremos con mucho frecuencia cuando trabaje con filtros pequeños y utilice la misma lógica en su aplicación. Si tu aplicación tiene demasiada lógica común, habrá como una tonelada de funciones pequeñas.

Pero en una aplicación donde no tiene una lógica común, no estará obligado a realizar pequeñas funciones, pero puede dividir su código en segmentos donde sea más fácil para su administración y comprensión.

En general, dividir su enorme código en una pequeña función es un muy buen enfoque. En los marcos e idiomas modernos estará obligado a hacerlo, por ejemplo,

data => initScroll(data)

es una función anónima en ES 2017 JavaScript y Typescript

getMarketSegments() {
 this.marketService.getAllSegments(this.project.id)
  .subscribe(data => this.segments = data, error => console.log(error.toString()));
}

en el código anterior puede ver 3 Declaración de función y 2 llamadas de función, esta es una simple llamada de servicio en Angular 4 con Typescript. Puedes considerarlo como tus requisitos

([] 0)
([x] 1)
([x y] 2)

Las anteriores son 3 funciones anónimas en lenguaje de clojure

(def hello (fn [] "Hello world"))

El anterior es una declaración funcional en clojure

Así que sí, las FUNCIONES pueden ser más pequeñas, pero si es buena o mala si tiene funciones como:

incrementNumber(numb) { return ++numb; }

Bueno, no es una buena práctica hacerlo, pero ¿qué sucede si está utilizando esta función en una etiqueta HTML como lo hacemos en Angular Framework si no hubiera soporte para Incremento o Disminución en Plantillas HTML Angulares? La solución para mí.

Tomemos otro ejemplo

insertInArray(array, newKey) {
 if (!array.includes(newKey)) {
   array.push(newKey);
 }
}

El ejemplo anterior es una necesidad cuando se juega cuando se hacen arreglos dentro de Plantillas de Angular HTML. Así que a veces tendrás que crear pequeñas funciones

    
respondido por el Anwaar Ulhaq 17.07.2017 - 10:54
-2

No estoy de acuerdo con casi la respuesta dada en este momento.

Recientemente encontré a un colega que escribe a todos los miembros de las clases como los siguientes:

 void setProperty(int value){ mValue=value; }
 int getProperty() const { return (mValue); }

Este podría ser un buen código en casos esporádicos, pero seguramente no si define muchas clases con muchas y muchas propiedades. No quiero decir, con esto, que no se escriban métodos como los anteriores. Pero, si terminas escribiendo la mayor parte del código como "envoltorio" de la lógica real, hay algo mal.

Probablemente el programador se pierda la lógica general, teme los cambios futuros y refactorice el código.

La definición de la interfaz es porque una necesidad real; de hecho, si necesita establecer y obtener una propiedad simple (sin mucha lógica, lo que hace que el miembro sea más corto) será posible. Pero, si la necesidad real se pudiera analizar de otra manera, ¿por qué no definir una interfaz más intuitiva?

Para responder realmente a la pregunta, por supuesto que no, y la razón es muy obvia: el método / propiedad / lo que sea, se definirá para lo que necesita. Incluso una función virtual vacía tiene una razón para existir.

    
respondido por el Luca 02.04.2011 - 00:15

Lea otras preguntas en las etiquetas