He notado que algunas funciones con las que trabajo tienen 6 o más parámetros, mientras que en la mayoría de las bibliotecas que uso es raro encontrar una función que requiera más de 3.
A menudo, muchos de estos parámetros adicionales son opciones binarias para alterar el comportamiento de la función. Creo que algunas de estas innumerables funciones parametrizadas probablemente deberían ser refactorizadas. ¿Hay una guía para qué número es demasiado?
functions
parameters
Darth Egregious
fuente
fuente
Respuestas:
Nunca he visto una directriz, pero en mi experiencia una función que toma más de tres o cuatro parámetros indica uno de dos problemas:
Es difícil saber lo que estás viendo sin más información. Lo más probable es que la refactorización que necesita hacer es dividir la función en funciones más pequeñas que se invocan desde el elemento primario dependiendo de los indicadores que actualmente se pasan a la función.
Al hacer esto, hay algunas buenas ganancias:
if
estructura que llama a muchos métodos con nombres descriptivos que una estructura que lo hace todo en un solo método.fuente
Según el "Código limpio: un manual de artesanía ágil de software", cero es el ideal, uno o dos son aceptables, tres en casos especiales y cuatro o más, ¡nunca!
Las palabras del autor:
En este libro hay un capítulo que habla solo de las funciones en las que se discuten los parámetros, por lo que creo que este libro puede ser una buena guía de cuántos parámetros necesita.
En mi opinión personal, un parámetro es mejor que nadie porque creo que está más claro lo que está sucediendo.
Como ejemplo, en mi opinión, la segunda opción es mejor porque es más claro lo que el método está procesando:
vs.
Acerca de muchos parámetros, esto puede ser una señal de que algunas variables se pueden agrupar en un solo objeto o, en este caso, muchos valores booleanos pueden representar que la función / método está haciendo más de una cosa, y en este caso, Es mejor refactorizar cada uno de estos comportamientos en una función diferente.
fuente
String language = detectLanguage(someText);
. En cualquiera de sus casos, pasó exactamente el mismo número de argumentos, simplemente sucede que ha dividido la ejecución de la función en dos debido a un lenguaje deficiente.Matrix.Create(input);
siinput
, por ejemplo, hay un .NETIEnumerable<SomeAppropriateType>
? De esa manera tampoco necesita una sobrecarga separada cuando desea crear una matriz que contenga 10 elementos en lugar de 9.Si las clases de dominio en la aplicación están diseñadas correctamente, la cantidad de parámetros que pasamos a una función se reducirá automáticamente, porque las clases saben cómo hacer su trabajo y tienen suficientes datos para hacer su trabajo.
Por ejemplo, supongamos que tiene una clase de gerente que le pide a una clase de tercer grado que complete las tareas.
Si modelas correctamente,
Esto es simple.
Si no tiene el modelo correcto, el método será así
El modelo correcto siempre reduce los parámetros de función entre las llamadas a métodos, ya que las funciones correctas se delegan a sus propias clases (responsabilidad única) y tienen suficientes datos para realizar su trabajo.
Cada vez que veo que aumenta el número de parámetros, verifico mi modelo para ver si diseñé mi modelo de aplicación correctamente.
Sin embargo, hay algunas excepciones: cuando necesito crear un objeto de transferencia u objetos de configuración, usaré un patrón de construcción para producir objetos construidos pequeños primero antes de construir un objeto de configuración grande.
fuente
Un aspecto que las otras respuestas no abordan es el rendimiento.
Si está programando en un lenguaje de nivel suficientemente bajo (C, C ++, ensamblaje), una gran cantidad de parámetros puede ser bastante perjudicial para el rendimiento en algunas arquitecturas, especialmente si la función se llama una gran cantidad de veces.
Cuando una llamada de función se realiza en ARM por ejemplo, los primeros cuatro argumentos se colocan en registros
r0
ar3
y argumentos restantes tienen que ser empujado en la pila. Mantener el número de argumentos por debajo de cinco puede marcar una gran diferencia para las funciones críticas.Para las funciones que se llaman muy a menudo, incluso el hecho de que el programa tiene que establecer los argumentos antes de cada llamada puede afectar al rendimiento (
r0
ar3
pueden ser sobrescritos por la función llamada y tendrá que ser reemplazado antes de la siguiente llamada) por lo que en ese sentido cero argumentos son los mejores.Actualizar:
KjMag trae a colación el interesante tema de la alineación. La alineación mitigará esto de alguna manera, ya que permitirá que el compilador realice las mismas optimizaciones que usted podría hacer si escribiera en ensamblado puro. En otras palabras, el compilador puede ver qué parámetros y variables utiliza la función llamada y puede optimizar el uso del registro para minimizar la lectura / escritura de la pila.
Sin embargo, hay algunos problemas con la alineación.
inline
fuera tratado como obligatorio en todos los casos.inline
o realmente le darán errores cuando lo encuentren.fuente
inline
)Cuando la lista de parámetros crezca a más de cinco, considere definir una estructura u objeto de "contexto".
Esto es básicamente una estructura que contiene todos los parámetros opcionales con algunos valores predeterminados razonables establecidos.
En el mundo procesal C, una estructura simple sería suficiente. En Java, C ++ un objeto simple será suficiente. No te metas con getters o setters porque el único propósito del objeto es mantener valores "públicos" configurables.
fuente
No, no hay una directriz estándar.
Pero hay algunas técnicas que pueden hacer que una función con muchos parámetros sea más soportable.
Puede usar un parámetro list-if-args (args *) o un parámetro dictionary-of-args (kwargs
**
)Por ejemplo, en python:
Salidas:
O podría usar la sintaxis de definición literal de objeto
Por ejemplo, aquí hay una llamada jQuery de JavaScript para lanzar una solicitud AJAX GET:
Si echa un vistazo a la clase ajax de jQuery, hay muchas (aproximadamente 30) propiedades más que se pueden configurar; principalmente porque las comunicaciones ajax son muy complejas. Afortunadamente, la sintaxis literal del objeto facilita la vida.
C # intellisense proporciona documentación activa de parámetros, por lo que no es raro ver arreglos muy complejos de métodos sobrecargados.
Los lenguajes de tipo dinámico como python / javascript no tienen esa capacidad, por lo que es mucho más común ver argumentos de palabras clave y definiciones literales de objetos.
Prefiero definiciones literales de objeto ( incluso en C # ) para administrar métodos complejos porque puede ver explícitamente qué propiedades se establecen cuando se instancia un objeto. Tendrá que hacer un poco más de trabajo para manejar los argumentos predeterminados, pero a la larga su código será mucho más legible. Con las definiciones literales de objetos, puede romper su dependencia de la documentación para comprender lo que hace su código a primera vista.
En mi humilde opinión, los métodos sobrecargados están muy sobrevalorados.
Nota: si recuerdo correctamente, el control de acceso de solo lectura debería funcionar para los constructores literales de objetos en C #. Básicamente funcionan igual que establecer propiedades en el constructor.
Si nunca ha escrito ningún código no trivial en un lenguaje basado en javaScript de tipo dinámico (python) y / o funcional / prototipo, le recomiendo que lo pruebe. Puede ser una experiencia esclarecedora.
Puede ser aterrador primero romper su dependencia de los parámetros para el enfoque final de la inicialización de funciones / métodos, pero aprenderá a hacer mucho más con su código sin tener que agregar una complejidad innecesaria.
Actualizar:
Probablemente debería haber proporcionado ejemplos para demostrar el uso en un lenguaje de tipo estático, pero actualmente no estoy pensando en un contexto de tipo estático. Básicamente, he estado haciendo demasiado trabajo en un contexto de tipo dinámico para cambiar repentinamente.
Lo que sí sé es que la sintaxis de definición literal de objetos es completamente posible en lenguajes estáticamente tipados (al menos en C # y Java) porque los he usado antes. En lenguajes tipados estáticamente se llaman 'Inicializadores de objetos'. Aquí hay algunos enlaces para mostrar su uso en Java y C # .
fuente
Personalmente, más de 2 es donde se dispara mi código de alarma de olor. Cuando considera que las funciones son operaciones (es decir, una traducción de entrada a salida), es raro que se usen más de 2 parámetros en una operación. Los procedimientos (que son una serie de pasos para lograr un objetivo) requerirán más aportes y, a veces, son el mejor enfoque, pero en la mayoría de los idiomas en estos días no deberían ser la norma.
Pero, de nuevo, esa es la guía más que una regla. A menudo tengo funciones que toman más de dos parámetros debido a circunstancias inusuales o facilidad de uso.
fuente
Al igual que dice Evan Plaice, soy un gran admirador de simplemente pasar matrices asociativas (o la estructura de datos comparable de su idioma) a funciones siempre que sea posible.
Por lo tanto, en lugar de (por ejemplo) esto:
Ir por:
Wordpress hace muchas cosas de esta manera, y creo que funciona bien. (Aunque mi código de ejemplo anterior es imaginario, y no es en sí mismo un ejemplo de Wordpress).
Esta técnica le permite pasar una gran cantidad de datos a sus funciones fácilmente, pero le libera de tener que recordar el orden en que cada uno debe pasar.
También apreciará esta técnica cuando llegue el momento de refactorizar; en lugar de tener que cambiar potencialmente el orden de los argumentos de una función (como cuando se da cuenta de que necesita pasar Yet Another Argument), no necesita cambiar el parámetro de sus funciones lista en absoluto.
Esto no solo le evita tener que volver a escribir la definición de su función, sino que le ahorra tener que cambiar el orden de los argumentos cada vez que se invoca la función. Esa es una gran victoria.
fuente
Una respuesta anterior mencionó a un autor confiable que declaró que cuantos menos parámetros tengan sus funciones, mejor será su desempeño. La respuesta no explica por qué, pero los libros lo explican, y aquí hay dos de las razones más convincentes de por qué necesita adoptar esta filosofía y con la que personalmente estoy de acuerdo:
Los parámetros pertenecen a un nivel de abstracción que es diferente del de la función. Esto significa que el lector de su código tendrá que pensar sobre la naturaleza y el propósito de los parámetros de sus funciones: este pensamiento es de "nivel inferior" que el del nombre y el propósito de sus funciones correspondientes.
La segunda razón para tener la menor cantidad posible de parámetros para una función es la prueba: por ejemplo, si tiene una función con 10 parámetros, piense cuántas combinaciones de parámetros tiene para cubrir todos los casos de prueba para, por ejemplo, una unidad prueba. Menos parámetros = menos pruebas.
fuente
Para proporcionar algo más de contexto en torno al consejo para que el número ideal de argumentos de función sea cero en el "Código limpio: un manual de artesanía ágil de software" de Robert Martin, el autor dice lo siguiente como uno de sus puntos:
Para su
includeSetupPage()
ejemplo anterior, aquí hay un pequeño fragmento de su "código limpio" refactorizado al final del capítulo:La "escuela de pensamiento" del autor argumenta a favor de clases pequeñas, bajo (idealmente 0) número de argumentos de función y funciones muy pequeñas. Si bien tampoco estoy totalmente de acuerdo con él, me pareció estimulante y creo que vale la pena considerar la idea de argumentos de función cero como ideal. Además, tenga en cuenta que incluso su pequeño fragmento de código anterior también tiene funciones de argumento distintas de cero, por lo que creo que depende del contexto.
(Y como otros han señalado, también argumenta que más argumentos lo hacen más difícil desde el punto de vista de la prueba. Pero aquí quería destacar principalmente el ejemplo anterior y su justificación para argumentos de función cero).
fuente
Idealmente cero. Uno o dos están bien, tres en ciertos casos.
Cuatro o más suele ser una mala práctica.
Además de los principios de responsabilidad única que otros han notado, también puede pensarlo desde las perspectivas de prueba y depuración.
Si hay un parámetro, conocer sus valores, probarlos y encontrar errores con ellos es relativamente fácil, ya que solo hay un factor. A medida que aumenta los factores, la complejidad total aumenta rápidamente. Para un ejemplo abstracto:
Considere un programa de "qué ponerse en este clima". Considere qué podría hacer con una entrada: temperatura. Como puede imaginar, los resultados de qué ponerse son bastante simples en función de ese único factor. Ahora considere lo que el programa podría / podría / debería hacer si se supera la temperatura, humedad, punto de rocío, precipitación, etc.
fuente