2011-01-14 16 views
6

Hice algunas búsquedas aquí y no he encontrado nada como esto, así que voy a seguir adelante y preguntar. Esto es más acerca de la semántica que una pregunta de programación real. Actualmente estoy escribiendo algo en C++ pero el lenguaje en realidad no importa.¿cuán corto debe ser una función?

Soy consciente de que es una buena práctica de programación mantener tus funciones/métodos lo más cortos posible. Sin embargo, ¿cómo sabes realmente si una función es demasiado larga? Alternativamente, ¿es posible romper las funciones demasiado?

El primer lenguaje de programación que aprendí (aparte de Applesoft BASIC, que no cuenta ...) fue el lenguaje de ensamblaje 6502, donde la velocidad y la optimización lo son todo. En los casos en que unos pocos recuentos de ciclos arruinan el tiempo de todo su programa, a menudo es mejor establecer una ubicación de memoria o registrarse directamente en lugar de saltar a otra subrutina. La primera operación podría tomar 3 o 4 ciclos, mientras que, en conjunto, la última podría tomar dos o tres veces más.

Aunque me doy cuenta de que hoy en día, si tuviera que mencionar los recuentos de ciclo a algunos programadores, me darían una mirada en blanco, es un hábito difícil de romper.

En concreto, supongamos que (de nuevo usando C++) tenemos un método de clase privada que es algo así como lo siguiente:

int Foo::do_stuff(int x) { 
    this->x = x; 
    // various other operations on x 
    this->y = this->x; 
} 

he visto algunos argumentos que, por lo menos, cada conjunto de operaciones deberían ser su propia función. Por ejemplo, do_stuff() debería en teoría nombrarse a set_x (int x), se debería escribir una función separada para el conjunto de operaciones realizadas en el miembro x de la clase, y se debería escribir una tercera función para asignar el valor final del miembro de la clase x a la clase miembro y. Pero he visto otros argumentos de que CADA operación debería tener su propia función.

Para mí, esto simplemente parece incorrecto. Una vez más, estoy mirando las cosas desde una perspectiva interna; cada llamada a método está presionando una dirección en la pila, realizando sus operaciones y luego regresando de la subrutina. Esto simplemente parece una gran sobrecarga para algo relativamente simple.

¿Existe una mejor práctica para este tipo de cosas o es más a juicio individual?

Respuesta

8

Desde los días del ensamblaje 6502, han ocurrido dos cosas: las computadoras se han vuelto mucho más rápidas, y los compiladores (según corresponda) se han vuelto más inteligentes.

Ahora el consejo es dejar de preocuparse por los ciclos individuales, hasta que esté seguro de que es un problema. Puedes pasar ese tiempo más sabiamente. Si me mencionas los recuentos de ciclos, no te veré en blanco porque no sé cuáles son. Te miraré preguntándote si estás desperdiciando tu esfuerzo.

su lugar, empezar a pensar en hacer sus funciones lo suficientemente pequeño para ser:

  • comprensible,
  • comprobable,
  • reutilizable, tal vez, cuando ello sea oportuno.

Si, posteriormente, encuentra que parte de su código no se está ejecutando lo suficientemente rápido, considere la forma de optimizarlo.

Nota: La optimización podría ser una sugerencia para el compilador para mover la función en línea, por lo que aún obtiene las ventajas anteriores, sin el golpe de rendimiento.

+0

Gracias, esto es definitivamente útil. C++ no es de ninguna manera mi primer lenguaje no ensamblador, pero dado que nunca tuve entrenamiento formal en nada, pensé que sería mejor obtener retroalimentación sobre las prácticas de codificación. – xobicvap

+1

+1 por decir que no debe preocuparse por los ciclos hasta que sea un problema. La optimización prematura es la fuente de todos los males. – riwalk

5

Lo más importante acerca de cómo dividir una función no es necesariamente cuánto funciona la función. Se trata más bien de determinar la API de tu clase.

Supongamos que rompemos Foo::do_stuff en Foo::set_x, Foo::twiddle_x y Foo::set_y. ¿Alguna vez tiene sentido hacer estas operaciones por separado? ¿Pasará algo malo si twiddle x sin configurarlo primero? ¿Puedo llamar al set_y sin llamar al set_x? Al dividirlos en métodos separados, incluso en métodos privados dentro de la misma clase, está dando a entender que son al menos operaciones potencialmente separadas.

Si ese no es el caso, entonces de todos modos, manténgalas en una función.

+1

+1. Mantener las invariantes y la consistencia del estado/los objetos es la clave del software que se puede mantener. –

+0

Esto es más o menos en la misma línea que lo que estaba pensando, pero quería asegurarme. Usando el ejemplo anterior, en una función que estoy escribiendo actualmente, no hay absolutamente ninguna razón para que set_y ocurra sin que set_x y twiddle_x hayan sido llamados primero. Habiendo venido de otros lenguajes (no OOP), * realmente * aprecio la encapsulación, ya que me ayuda a definir en mi cabeza exactamente cómo una variable particular debe ser alterada y qué tiene derecho a alterarla. Tengo un poco de miedo de ir * demasiado * por la borda con eso. Estas pautas deberían ayudar. – xobicvap

0

Creo que en cualquier base de código, la legibilidad es mucho más preocupante que algunos ciclos de reloj más por todas las razones conocidas, mantenimiento principal, que es donde la mayoría del código pasa su tiempo, y como resultado donde más dinero se gasta en el código. así que estoy evadiendo tu pregunta diciendo que las personas no se preocupan por eso porque es insignificante en comparación con otras preocupaciones [más corporativas].

aunque si ponemos a un lado otras preocupaciones, hay declaraciones en línea explícitas, y más importante aún, optimizaciones del compilador que a menudo pueden eliminar la mayor parte de la sobrecarga relacionada con las llamadas a funciones triviales. el compilador es una máquina de optimización increíblemente inteligente y, a menudo, organiza las llamadas a funciones mucho más inteligentemente de lo que podríamos hacerlo.

+0

Estoy de acuerdo, aunque para hacer de abogado del diablo, me gustaría decir que creo que al menos un conocimiento del lenguaje ensamblador es definitivamente útil ya que al menos tienes alguna idea de cómo funcionan las cosas a nivel de la CPU. Al mismo tiempo, no echo de menos la programación de ensambles. La legibilidad es prácticamente nula en el código ASM, y no solo por la terminología. – xobicvap

+0

@ubtng, de acuerdo, no tenía la intención de dar a entender lo contrario. mi punto era que cuando la gente va al tablero de dibujo, la descomposición de la función no ocupa un lugar destacado en la lista de prioridades. en lo que a mí respecta, ¡el montaje es genial! ilegible, pero genial! – davin

3

soy muy consciente de que es una buena práctica de programación para mantener sus funciones /métodos lo más corto posible

yo no usaría los criterios anteriores refactorizar mis funciones para los más pequeños. A continuación es lo que uso

  1. mantener todas las funciones al mismo nivel de abstracción
  2. asegurarse de que no sin efectos secundarios (por excepcionales casos asegúrese de forma explícita documento de ellas)
  3. Asegúrese una función no está haciendo más de una cosa (Principio de SRP). Pero puedes romper esto para honrar 1.

Otros good practices for Method Design

  1. No hacer que el cliente recibe ninguna orden el módulo podía hacer
  2. no violen el principio de mínima asombro
  3. fallan errores Fast-informe lo antes como Posible después de que ocurran
  4. Sobrecarga con cuidado
  5. uso apropiados de los parámetros y valores de retorno
  6. Uso de parámetros consistente pedidos Al otro lado de Métodos
  7. evitar largos Listas de parámetros
  8. evitar valores de retorno que la demanda Procesamiento excepcional
2

Después de leer Clean Code: A Handbook of Agile Software Craftsmanship el que tocaron casi todas las piezas de consejos en esta página comencé a escribir más corto. No por el simple hecho de tener funciones cortas, sino para mejorar la legibilidad y la capacidad de prueba, mantenerlos en el mismo nivel de abstracción, hacer que hagan una sola cosa, etc.

Lo que me ha resultado más gratificante es que me encuentro escribiendo mucha menos documentación porque simplemente no es necesario para el 80% de mis funciones. Cuando la función solo hace lo que dice su nombre, no tiene sentido repetir lo obvio. Escribir pruebas también es más fácil porque cada método de prueba puede configurar menos y realizar menos afirmaciones. Cuando reviso el código que he escrito durante los últimos seis meses con estos objetivos en mente, puedo realizar más rápidamente el cambio que quiero y seguir.

Cuestiones relacionadas