¿Cuántos parámetros son demasiados? [cerrado]

228

Las rutinas pueden tener parámetros, eso no es noticia. Puede definir tantos parámetros como necesite, pero demasiados de ellos harán que su rutina sea difícil de entender y mantener.

Por supuesto, puede usar una variable estructurada como solución alternativa: poner todas esas variables en una sola estructura y pasarla a la rutina. De hecho, el uso de estructuras para simplificar las listas de parámetros es una de las técnicas descritas por Steve McConnell en Code Complete . Pero como él dice:

Los programadores cuidadosos evitan agrupar datos más de lo lógicamente necesario.

Entonces, si su rutina tiene demasiados parámetros o usa una estructura para disfrazar una gran lista de parámetros, probablemente esté haciendo algo mal. Es decir, no mantienes el acoplamiento suelto.

Mi pregunta es, ¿ cuándo puedo considerar una lista de parámetros demasiado grande? Creo que más de 5 parámetros, son demasiados. ¿Qué piensas?

Auron
fuente
1
Simplemente haciendo referencia aquí a esta pregunta, que es un ejemplo práctico de cuántos parámetros son demasiados ...
Gideon
55
En JavaScript 65537 los parámetros son demasiados: jsfiddle.net/vhmgLkdm
Aadit M Shah
basta con mirar los componentes http de apache, es casi imposible construir algo dinámico a partir de él
Andrew Scott Evans

Respuestas:

162

¿Cuándo se considera algo tan obsceno como algo que se puede regular a pesar de la garantía de la 1ra Enmienda a la libertad de expresión? Según el juez Potter Stewart, "lo sé cuando lo veo". Lo mismo vale aquí.

Odio hacer reglas duras y rápidas como esta porque la respuesta cambia no solo según el tamaño y el alcance de su proyecto, sino que creo que cambia incluso hasta el nivel del módulo. Dependiendo de lo que esté haciendo su método, o de lo que se supone que representa la clase, es muy posible que 2 argumentos sean demasiados y sea un síntoma de demasiado acoplamiento.

Sugeriría que al hacer la pregunta en primer lugar, y calificar su pregunta tanto como lo hizo, que realmente sabe todo esto. La mejor solución aquí es no confiar en un número duro y rápido, sino buscar revisiones de diseño y revisiones de código entre sus pares para identificar áreas donde tiene baja cohesión y acoplamiento estrecho.

Nunca tengas miedo de mostrar a tus colegas tu trabajo. Si tiene miedo, esa es probablemente la señal más grande de que algo está mal con su código y que ya lo sabe .

Nick
fuente
Una buena regla general es la cantidad de registros de la CPU, ya que el compilador se verá obligado a asignarlos en la pila.
Michaelangel007
1
@ Michaelangel007 con respecto a la respuesta que está comentando, no debe decir eso, porque dice que no hay una regla. Además, el número de parámetros es una cuestión de legibilidad, no de rendimiento.
clemp6r
1
@ clemp6r Incorrecto: son ambos . Los compiladores tienen que lidiar con el "registro de derrames" todo el tiempo. La única respuesta autorizada es inspeccionar el ensamblaje de lo que está generando su compilador. El número de registros no cambia mágicamente en la misma plataforma. en.wikipedia.org/wiki/Register_allocation
Michaelangel007
124

Una función solo puede tener demasiados parámetros si algunos de los parámetros son redundantes. Si se utilizan todos los parámetros, la función debe tener el número correcto de parámetros. Tome esta función de uso frecuente:

HWND CreateWindowEx
(
  DWORD dwExStyle,
  LPCTSTR lpClassName,
  LPCTSTR lpWindowName,
  DWORD dwStyle,
  int x,
  int y,
  int nWidth,
  int nHeight,
  HWND hWndParent,
  HMENU hMenu,
  HINSTANCE hInstance,
  LPVOID lpParam
);

Son 12 parámetros (9 si agrupa los x, y, w y h como un rectángulo) y también están los parámetros derivados del nombre de la clase. ¿Cómo reducirías esto? ¿Desea reducir el número más al punto?

No deje que la cantidad de parámetros le moleste, solo asegúrese de que sea lógico y esté bien documentado y deje que intellisense * lo ayude.

* ¡ Hay otros asistentes de codificación disponibles!

Skizz
fuente
37
Estoy votando esto. ¡Estoy sorprendido por todas las otras respuestas que dicen "3" y "4"! La respuesta correcta es: el mínimo necesario, que a veces puede ser bastante.
Tony Andrews
16
Si esa función fuera diseñada hoy, probablemente se vería un poco diferente. x, y, nWidth y nHeight se pueden agrupar en un objeto Rectangle. style y xStyle podrían combinarse en un conjunto de enumeraciones o cadenas. Ahora solo tienes 8 parámetros.
Finnw
16
@finnw Si esa función fuera diseñada hoy? Ya ha sido rediseñado. Formulario f = nuevo Formulario (); Tiene 0 parámetros.
Nick
36
Esto es C y Winapi. No puedo pensar en un peor ejemplo.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
17
No no. Este es el estilo de procedimiento. Las ventanas son objetos, por lo que ahora es obsoleta. Hoy, esto se resolvería con clases agregadas, no con muchos parámetros. La regla intuitiva de un buen diseño es que si no puede describir la función (incluidos los parámetros) en una oración simple, está mal diseñada . Creo que este es el caso.
Jan Turoň
106

En Clean Code , Robert C. Martin dedicó cuatro páginas al tema. Aquí está la esencia:

El número ideal de argumentos para una función es cero (niládico). Luego viene uno (monádico), seguido de cerca por dos (diádico). Se deben evitar tres argumentos (triádicos) siempre que sea posible. Más de tres (poliádico) requieren una justificación muy especial, y de todos modos no deberían usarse.

Patrick McElhaney
fuente
2
Dudo que incluya "esto" porque ese es el contexto de ejecución. En la programación funcional, el contexto es global y en oop, el contexto es el objeto en el que aplica el método. Además, si incluye "esto" en la lista de parámetros, entonces sería imposible tener 0 parámetros (ideal).
Tom
1
No, no incluye "esto".
Patrick McElhaney el
20
Además, Martin debería hablar con el comité estándar de C ++. La mitad de <algorithm> toma más de 3 parámetros, porque solo un rango de iterador ya es 2. Es solo la naturaleza de la programación con iteradores (es decir, programación genérica con colecciones STL).
Steve Jessop
3
@SteveJessop: el error de <algorithm> es que siempre funciona en un segmento. Si el algoritmo y la colección fueran rediseñados, haría que los algoritmos siempre tomaran una colección completa, y obtendrías una forma de cortar una vista de una colección para permitir que los algoritmos funcionen en partes; alternativamente, también definiría una anulación abreviada para facilitar el trabajo en el caso común.
Lie Ryan
3
@LieRyan: mientras que si estuviera rediseñando <algorithm>lo haría actuar sobre un objeto de rango. Las colecciones serían rangos, pero no todos los rangos serían colecciones. Y de hecho, el impulso ya lo ha hecho. De todos modos, mi punto es que una biblioteca masivamente utilizado hace caso omiso de este consejo, por lo que el peor que ha garantizado que te ocurra si lo hace también, es que muchos de sus millones de usuarios va a jugar con simplificaciones menores en su interfaz ;-)
Steve Jessop
79

Algunos códigos con los que he trabajado en el pasado usaban variables globales solo para evitar pasar demasiados parámetros.

¡Por favor no hagas eso!

(Generalmente.)

Jeffrey L Whitledge
fuente
Algún código en el que estaba trabajando utilizó miembros de la clase para lograr lo mismo. En ese momento yo era un programador en C y pregunté, ¿por qué hay tantas variables globales, por qué las entradas / salidas no pueden ser parámetros explícitamente?
user3528438
38

Si comienza a tener que contar mentalmente los parámetros en la firma y hacerlos coincidir con la llamada, ¡es hora de refactorizar!

Rob Walker
fuente
Esa es una muy buena respuesta. Si los parámetros están organizados lógicamente (x, y, w, h) es fácil recordarlos todos en el orden correcto. Es mucho más difícil recordar dónde colocar el puntero FILE en putc (que solo tiene dos parámetros), especialmente porque fprintf es lo contrario.
user877329
Buena respuesta. Muy bien dicho
Anwar
31

Muchas gracias por todas sus respuestas:

  • Fue un poco sorprendente encontrar personas que también piensan (como yo) que 5 parámetros es un buen límite para la cordura del código.

  • En general, las personas tienden a aceptar que un límite entre 3 y 4 es una buena regla general. Esto es razonable ya que las personas suelen pasar un mal rato contando más de 4 cosas.

  • Como señala Milan , en promedio las personas pueden tener más o menos 7 cosas en su cabeza a la vez. Pero creo que no puedes olvidar que, cuando estás diseñando / manteniendo / estudiando una rutina, debes tener en cuenta más cosas que solo los parámetros.

  • Algunas personas consideran que una rutina debería tener tantos argumentos como sea necesario. Estoy de acuerdo, pero solo por unos pocos casos específicos (llamadas a API del sistema operativo, rutinas donde la optimización es importante, etc.). Sugiero ocultar la complejidad de estas rutinas agregando una capa de abstracción justo encima de estas llamadas siempre que sea posible.

  • Nick tiene algunas ideas interesantes sobre esto. Si no quieres leer sus comentarios, te lo resumo: en pocas palabras, depende de :

    Odio hacer reglas duras y rápidas como esta porque la respuesta cambia no solo según el tamaño y el alcance de su proyecto, sino que creo que cambia incluso hasta el nivel del módulo. Dependiendo de lo que esté haciendo su método, o de lo que se supone que representa la clase, es muy posible que 2 argumentos sean demasiados y sea un síntoma de demasiado acoplamiento.

    La moraleja aquí es no tener miedo de mostrar su código a sus compañeros, discutir con ellos e intentar "identificar áreas en las que tenga poca cohesión y un acoplamiento estrecho" .

  • Finalmente, creo que wnoise está muy de acuerdo con Nick, y concluye su contribución satírica con esta visión poética (ver comentarios más abajo) del arte de la programación:

    La programación no es ingeniería. La organización del código es un arte porque depende de factores humanos, que dependen demasiado del contexto para cualquier regla difícil.

Auron
fuente
16

Esta respuesta supone un lenguaje OO. Si no está usando una, omita esta respuesta (en otras palabras, esta no es una respuesta independiente del idioma).

Si está pasando más de 3 parámetros más o menos (especialmente tipos / objetos intrínsecos), no es que sea "Demasiados", sino que puede estar perdiendo la oportunidad de crear un nuevo objeto.

Busque grupos de parámetros que se pasan a más de un método; incluso un grupo pasado a dos métodos casi garantiza que debería tener un nuevo objeto allí.

Luego refactoriza la funcionalidad en su nuevo objeto y no creería cuánto ayuda tanto a su código como a su comprensión de la programación OO.

Bill K
fuente
Por favor, nombra un idioma que no use rutinas ni parámetros. No puedo pensar en uno, incluso el ensamblaje podría considerarse que tiene rutinas (etiquetas).
Auron
El ensamblaje x86 definitivamente tiene rutinas (llamada / retorno). Otros pueden requerir combos cmp / jmp.
Brian Knoblauch
Lo sentimos, "ret", no "return". Suspiros Ya ha sido un día largo.
Brian Knoblauch el
Perdón por la redacción ambigua de Auron, creo que lo arreglé. Mi respuesta fue específica del idioma, no la pregunta.
Bill K
1
No todos los idiomas permiten pasar estructuras. Además, pasar una estructura significa que el método de recepción debe tener el código para manejar la estructura y, por lo tanto, puede necesitar más parámetros. Además, en un idioma que no sea oo, generalmente necesita un parámetro adicional: todos los idiomas OO tienen un parámetro "Oculto" de "esto" que de otro modo debería pasarse (o, en un lenguaje / diseño más horrible, podría ser globalmente accesible)
Bill K
13

Parece que hay otras consideraciones además del mero número, aquí hay algunas que vienen a la mente:

  1. relación lógica con el propósito principal de la función frente a configuraciones únicas

  2. Si son solo indicadores del entorno, la agrupación puede ser muy útil

John Mulder
fuente
12

Uno de los conocidos epigramas de programación de Alan Perlis (relatado en ACM SIGPLAN Notices 17 (9), septiembre de 1982) afirma que "si tiene un procedimiento con 10 parámetros, probablemente se haya perdido alguno".

Peter S. Housel
fuente
11

Según Steve McConnell en Code Complete , deberías

Limite el número de parámetros de una rutina a aproximadamente siete

Paul Reiners
fuente
3
/: Número que indica que el factoid está compuesto: xkcd.com/899
user877329
9

Para mí, cuando la lista cruza una línea en mi IDE, entonces es un parámetro demasiado. Quiero ver todos los parámetros en una línea sin romper el contacto visual. Pero esa es solo mi preferencia personal.

Aprendizaje
fuente
Esto es bueno hasta que con algunos desarrolladores intentes foo (int a, float b, string c, double d). Supongo que es mejor tratar de evitar trabajar con ellos. : D
Rontólogo
44
Si alguien más ha dado a las clases nombres ridículamente largos, no dejaría que eso influya en cómo defino o llamo mis rutinas.
Finnw
99
¿Puedo presentarle el "retorno de carro"?
3
Cuando la lista cruza una línea en su IDE, entonces su monitor es demasiado pequeño para usarlo con ese código y claramente debe comprar un monitor con una resolución horizontal más alta. ¡Un salto de línea o una reducción de los parámetros son solo soluciones alternativas que no resuelven el problema raíz, que es que su monitor es demasiado pequeño para esa base de código!
Kaiserludi
9

Generalmente estoy de acuerdo con 5, sin embargo, si hay una situación en la que necesito más y es la forma más clara de resolver el problema, entonces usaría más.

Inisheer
fuente
8

¿Siete cosas en la memoria a corto plazo?

  1. Nombre de la función.
  2. Valor de retorno de la función
  3. Propósito de la función.
  4. Parámetro 1
  5. Parámetro 2
  6. Parámetro 3
  7. Parámetro 4
Mike Clark
fuente
Bien. Es una regla de oro. Cuando codifica el cuerpo de una función, no le importa su nombre, y el valor de retorno y su propósito están estrechamente relacionados.
Auron
77
8. orden de parámetros
Eva
7

En los peores 5 fragmentos de código , marque el segundo, "¿Es este un constructor". Tiene más de 37 ⋅ 4 ≈ 150 parámetros:

Aquí un programador escribió este constructor [...] Algunos de ustedes pueden pensar que sí, es un gran constructor pero usó las herramientas de generación automática de código de eclipse [.] NOO, en este constructor había un pequeño error que descubrí, lo que me hizo Concluimos que este constructor fue escrito a mano. (por cierto, esta es solo la parte superior del constructor, no está completa).

constructor con más de 150 parámetros

medopal
fuente
Esto es muy triste ... No saber sobre "registros" o "estructuras" u "objetos de valor", que agrupan varios valores y les dan un nombre común para que pueda representar jerárquicamente muchos de ellos de una manera legible es como caminar. con zapatos sin cordones durante años porque nadie te dijo que podías hacer eso that
yeoman
6

Uno más de lo necesario. No me refiero a ser simplista, pero hay algunas funciones que necesariamente necesitan bastantes opciones. Por ejemplo:

void *
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset);

Hay 6 argumentos, y cada uno de ellos es esencial. Además, no existe un vínculo común entre ellos para justificar su agrupación. Tal vez podría definir "struct mmapargs", pero eso sería peor.

Kirk Strauser
fuente
Bueno, proty flagspodría haberse unido si el diseñador creyera que 5 era de alguna manera un número mágico que es mucho mejor que 6. Un poco como la forma en que opencombina el modo de lectura / escritura con todas las otras banderas misceláneas. Y tal vez podría deshacerse offsetespecificando que la sección asignada comienza en la posición de búsqueda actual de filedes. No sé si hay situaciones en las que puedes hacer mmapuna región de la que no eres capaz lseek, pero si no es así, no es estrictamente necesario.
Steve Jessop
... así que creo que mmapes una buena ilustración del hecho de que algunos diseñadores y usuarios prefieren una larga lista de parámetros, mientras que otros prefieren seguir algunos pasos preparando un número menor de parámetros antes de hacer la llamada.
Steve Jessop
1
@Steve: Establecer la posición de búsqueda con una llamada por separado de antemano habría introducido una condición de carrera gratuita. Para algunas API (OpenGL), hay tantos parámetros que afectan a una llamada que realmente tiene que usar el estado, pero normalmente, cada llamada debe estar sola tanto como sea posible. La acción a distancia es el camino hacia el lado oscuro.
Ben Voigt
5

Según las mejores prácticas de Perl , 3 está bien, 4 es demasiado. Es solo una guía, pero en nuestra tienda es a lo que tratamos de apegarnos.

Adam Bellaire
fuente
5

Yo mismo dibujaría el límite para las funciones públicas en 5 parámetros.

En mi humilde opinión, las listas largas de parámetros solo son aceptables en funciones auxiliares privadas / locales que solo deben llamarse desde algunos lugares específicos del código. En esos casos, es posible que deba transmitir mucha información del estado, pero la legibilidad no es una gran preocupación ya que solo usted (o alguien que mantendrá su código y debe comprender los fundamentos de su módulo) debe preocuparse por llamando a esa función.

Dormir
fuente
Es exactamente por eso que mucha gente en este punto haría todo lo que no es un argumento real, sino un estado transmitido, solo el estado de un objeto en forma de campos (variables miembro). Ese objeto se representaría como una clase. Se instanciaría una vez o para cada uso. A veces, dicho objeto solo tendrá un método privado o público de paquete, que luego delega el trabajo a métodos privados con pocos argumentos cada uno. Pocos argumentos ahora son posibles porque la configuración y el estado ahora tienen un lugar apropiado :)
yeoman
5

Una pregunta relacionada que debe considerar es qué tan cohesiva es la rutina. Una gran cantidad de parámetros puede ser un olor que le dice que la rutina en sí está tratando de hacer demasiado y, por lo tanto, su cohesión es sospechosa. Estoy de acuerdo en que un número rápido y difícil de parámetros es probablemente imposible, pero supongo que una rutina de alta cohesión implicaría un número bajo de parámetros.

Onorio Catenacci
fuente
4

Me detengo en tres parámetros como regla general. Además, es hora de pasar una matriz de parámetros o un objeto de configuración, lo que también permite agregar parámetros futuros sin cambiar la API.

Eran Galperin
fuente
Si la API cambia, entonces la API debería cambiar, no solo tener un cambio sigiloso donde la incompatibilidad aún podría ocurrir, sino ser menos obvio.
wnoise
sin embargo, si necesita un parámetro más para configurar un caso perimetral, no debe propagarse a componentes no relacionados utilizando la API
Eran Galperin
4

Una restricción de longitud en una lista de parámetros es solo una restricción más. Y restricción significa violencia aplicada. Suena divertido, pero puedes ser no violento incluso cuando estás programando. Simplemente deje que el código dicte las reglas. Es obvio que si tiene muchos parámetros, el cuerpo del método de función / clase será lo suficientemente grande como para utilizarlos. Y los fragmentos de código grandes generalmente se pueden refactorizar y dividir en fragmentos más pequeños. Por lo tanto, obtiene una solución contra tener muchos parámetros como bonificación gratuita, ya que se dividen entre los fragmentos de código refactorizados más pequeños.

Anónimo
fuente
4

Una cosa que señalaría desde una perspectiva de rendimiento es que, dependiendo de cómo pase los parámetros a un método, pasar muchos parámetros por valor ralentizará el programa porque cada parámetro debe copiarse y luego colocarse en la pila.

¡Usar una sola clase para abarcar todos los parámetros funcionaría mejor porque un solo parámetro pasado por referencia sería elegante, más limpio y más rápido!

Dominic Zukiewicz
fuente
Le doy a esta respuesta un +1 porque es la única que discute algo además de la limpieza del código o los límites arbitrarios, que son subjetivos. Es posible que algunas personas no piensen en lo que le está haciendo a la pila cuando está en un ciclo y empuja docenas de argumentos dentro y fuera de la pila. Si está en un bucle, debería considerar usar el número de argumentos que se pasan por REGISTERS en el ABI para el que está compilando. Por ejemplo, en el MS x64 ABI, el máximo de args pasados ​​a través de los registros es 4. El "System V" ABI (usado por un sistema operativo que no es Windows) usa más registros, por lo que el uso de 4 args es bastante portátil
Lakey
3

Según yo, podría haber casos en los que exceda 4 o algún número fijo. Cosas a vigilar podrían ser

  1. Su método está haciendo demasiado y necesita refactorizar.
  2. Es posible que desee considerar el uso de una colección o alguna estructura de datos.
  3. Reconsidere el diseño de su clase, tal vez algunas cosas no tengan que pasarse por alto.

Desde el punto de vista de la facilidad de uso o la facilidad de lectura del código, creo que cuando necesita "ajustar" la firma de su método, eso debería hacer que se detenga y piense, a menos que se sienta impotente y todos los esfuerzos para hacer que la firma sea más pequeña conducen a sin resultados. Algunas bibliotecas muy buenas en pasado y presente usan más de 4-5 cochecitos.

Codificador perpetuo
fuente
3

Mi regla general es que necesito poder recordar los parámetros el tiempo suficiente para mirar una llamada y decir lo que hace. Entonces, si no puedo mirar el método y luego pasar a una llamada de un método y recordar qué parámetro hace qué, entonces hay demasiados.

Para mí eso equivale a aproximadamente 5, pero no soy tan brillante. Su experiencia puede ser diferente.

Puede crear un objeto con propiedades para mantener los parámetros y pasarlo si excede el límite establecido. Vea el libro de Refactorización de Martin Fowler y el capítulo sobre cómo hacer que las llamadas a métodos sean más simples.

Mike dos
fuente
1

Depende en gran medida del entorno en el que esté trabajando. Tome, por ejemplo, javascript. En javascript, la mejor manera de pasar parámetros es usar objetos con pares clave / valor, lo que en la práctica significa que solo tiene un parámetro. En otros sistemas, el punto óptimo será a las tres o cuatro.

Al final, todo se reduce al gusto personal.

Joeri Sebrechts
fuente
1

Estoy de acuerdo con que 3 está bien, 4 es demasiado como guía. Con más de 3 parámetros, inevitablemente está haciendo más de una tarea. Más de una tarea debe dividirse en métodos separados.

Sin embargo, si mirara el último proyecto en el que he trabajado, las excepciones abundarían y la mayoría de los casos serían difíciles de reducir a 3 parámetros.

kae
fuente
1

Si tengo 7-10 parámetros en una rutina, considero agruparlos en una nueva clase, pero no si esa clase no sería más que un montón de campos con captadores y establecedores: la nueva clase tiene que hacer algo más que mezclar valores y fuera. De lo contrario, preferiría soportar la larga lista de parámetros.

finnw
fuente
1
Lo agruparé con una clase de solo datos si se usa en más de un lugar, pero incluso entonces generalmente creo ambos constructores.
Leahn Novash el
1

Es un hecho conocido que, en promedio, las personas pueden tener 7 +/- 2 cosas en su cabeza a la vez. Me gusta usar ese principio con los parámetros. Suponiendo que todos los programadores sean personas inteligentes superiores a la media, diría que todo 10+ es demasiado.

Por cierto, si los parámetros son similares de alguna manera, los pondría en un vector o lista en lugar de una estructura o clase.

Milan Babuškov
fuente
1

Basaría mi respuesta en la frecuencia con que se llama la función.

Si se trata de una función init que solo se llama una vez, deje que tome 10 parms o más, a quién le importa.

Si se llama varias veces por fotograma, entonces tiendo a hacer una estructura y simplemente le paso un puntero, ya que eso tiende a ser más rápido (suponiendo que no esté reconstruyendo la estructura también cada vez).

KPexEA
fuente
1

Según Jeff Bezos, de la fama de Amazon, no se puede alimentar más de dos pizzas :

Kevin Pang
fuente