¿Cuál es la mejor práctica para ordenar parámetros en una función?

52

A veces (raramente), parece que crear una función que tome una cantidad decente de parámetros es la mejor ruta. Sin embargo, cuando lo hago, siento que a menudo elijo el orden de los parámetros al azar. Por lo general, voy por "orden de importancia", con el parámetro más importante primero.

¿Hay una mejor manera de hacer esto? ¿Existe una forma de "mejor práctica" de ordenar parámetros que mejore la claridad?

Casey Patton
fuente
55
Los parámetros con nombre hacen que esto sea menos que un problema. Python lo lleva al extremo, míralo. Un buen ejemplo está en C #: las muchas formas de llamar MessageBox.Show. Mira eso también.
Trabajo
8
En Haskell, la posible utilidad de la aplicación de funciones parciales generalmente sugiere un ordenamiento de argumento natural.
tdammers
¿Qué considerarías una cantidad decente de parámetros?
Chris
Estaba pensando> 2. Aunque supongo que el orden se aplica a 2 parámetros de todos modos.
Casey Patton
3
@tdammers esto también es cierto para cualquier lenguaje que tenga soporte directo para curry
jk.

Respuestas:

55

En general: utilízalo .

Escriba una prueba para su función, una prueba del mundo real.
Algo que realmente le gustaría hacer con esa función .

Y vea en qué orden los dejó.

A menos que ya tenga (o sepa) algunas funciones que hacen algo similar.
En ese caso: conforme a lo que ya hacen, al menos para los primeros argumentos.

Por ejemplo, ¿toman todos ellos un documento / objeto / puntero de archivo / serie de valores / coordenadas como el primer argumento (s)? Por el amor de Dios, conforme a esos argumentos .

Evita confundir a tus compañeros de trabajo y a ti mismo en el futuro .

ZJR
fuente
12
"Evitar confundir ... tu futuro yo" suena como una buena filosofía en general.
Jeanne Pindar
28

Por lo general, sigo estas reglas, aunque no siempre con la misma precedencia. Supongo que es un proceso de pensamiento automático ahora, y no lo pienso demasiado, excepto por el diseño de API pública .

Embudo de selección

  1. Semántica
  2. Importancia / Relevancia
  3. Frecuencia de uso
  4. Preocupaciones de E / S

1. La semántica primero

Especialmente en OOP, elija parámetros basados ​​en su significado semántico para la acción o el mensaje. La firma de un método bien nombrado con un parámetro bien nombrado debe:

  • sentirse natural de llamar,
  • ser autodescriptivo en términos de intención y comportamiento.

(Por estas razones, a veces el uso de tipos o alias personalizados en lugar de primitivos puede aumentar la expresividad de su firma).

2. Entonces importancia

El parámetro más "significativo" viene primero (o después ...)

3. Luego frecuencia

La frecuencia también es importante, especialmente en un lenguaje donde no tiene parámetros con nombre pero puede tener valores predeterminados en los parámetros posicionales. Eso implica que el orden de los parámetros no varía, y que obviamente no puede establecer los parámetros N + 1 si desea forzar el valor predeterminado del parámetro Nth (excepto si su idioma tiene un concepto de parámetro de marcador de posición )

La buena noticia para usted es que, por lo general, la frecuencia se relaciona con la importancia, por lo que va de la mano con el punto anterior. Y luego probablemente depende de usted diseñar su API para que tenga la semántica adecuada.

4. No olvidemos las E / S

si su método / función toma alguna entrada y produce una salida, y esta última no se debe "devolver" (a través de una declaración de retorno) o "arrojar" (utilizando un sistema de excepción), entonces tiene la opción de pasar valores de nuevo a la persona que llama utilizando sus otros parámetros (o el parámetro de entrada). Eso se relaciona con la semántica, y en la mayoría de los casos tendrá sentido que los primeros parámetros definan la salida, y los últimos parámetros reciban la salida.

Además, otro enfoque para tener menos parámetros y maximizar la semántica sería utilizar un enfoque funcional, o definir un patrón Builder , para que pueda apilar claramente sus entradas, definir sus salidas y recuperarlas cuando sea necesario.

(Tenga en cuenta que no menciono las variables globales, porque ¿por qué usaría una, verdad?)


Algunas cosas a considerar

  • Usabilidad

    La mayoría de lo anterior se mostrará de forma natural si sigue los consejos de ZJR : ¡ Úselo!

  • Considere refactorizar

    Si le preocupa el orden de los parámetros, tal vez esta preocupación encuentre su raíz en lo anterior y en el hecho de que su API está mal diseñada. Si tiene demasiados parámetros, es probable que algo pueda ser componente / modularizado y refactorizado .

  • Considere el rendimiento

    Tenga en cuenta que las implementaciones de algunos idiomas tendrán un impacto muy importante en la administración de la memoria en tiempo de ejecución al usar parámetros. De ahí la razón por la cual los libros de estilo de muchos idiomas recomiendan mantener la lista de parámetros simple y corta . Con 4 parámetros como máximo, por ejemplo. Lo dejo como ejercicio para que entiendas por qué.

  • ¡La respuesta de Bevan y la mención de las recomendaciones de Clean Code también son definitivamente relevantes!

haylem
fuente
18

Respetuosamente afirmo que preocuparse por el orden de los parámetros es preocuparse por lo incorrecto.

En el libro " Código limpio " del tío Bob , defiende, persuasivamente, que los métodos nunca deberían tener más de dos argumentos, y la mayoría debería tener solo uno, si es que tiene alguno. Cuando este es el caso, el orden es obvio o sin importancia.

Sin embargo, de manera imperfecta, estoy tratando de seguir el consejo del tío Bob, y está mejorando mi código.

En los raros casos en los que un método parece requerir más información, es una buena idea introducir un objeto de parámetro . Por lo general, creo que este es el primer paso hacia el descubrimiento de un nuevo concepto (objeto) que es clave para mi algoritmo.

Bevan
fuente
¡Definitivamente estoy de acuerdo contigo! Mi primera declaración tenía como objetivo llegar al punto de que esto no es algo típico para mí, jaja. Pero en el caso de que descubra que realmente necesito pasar algunos parámetros y no vale la pena refactorizar todo el programa, me pregunto si puedo ordenar.
Casey Patton
3
tos PHP tos . Lo siento, ¿estabas diciendo algo sobre no preocuparte por el orden de los parámetros?
Craige
¿Entonces no tendrá un constructor para su objeto de parámetro que tome tantos argumentos?
desvío
No, porque el objeto de parámetro se puede "construir" en varias declaraciones de una manera que sea más legible.
Bevan
2
@Bevan: esto es muy discutible. Construir objetos de esta manera significa que ya no pueden ser inmutables; Sin embargo, mantener sus objetos inmutables siempre que sea posible es otra excelente forma de aumentar la capacidad de mantenimiento.
tdammers
10

Intento poner los parámetros IN primero, los parámetros OUT después. También hay algunos ordenamientos naturales, por ejemplo, createPoint(double x, double y)es altamente preferible createPoint(double y, double x).

cuant_dev
fuente
55
A veces, lo opuesto es mejor, por ejemplo, cuando siempre hay un solo argumento OUT y un número variable de argumentos in, como in addAllTo(target, something1, something2).
maaartinus
Aunque sigo la misma regla, trato de evitar los parámetros OUT siempre que sea posible, y la mayoría de los buenos programadores también. Por lo tanto, el número de funciones donde esta recomendación realmente ayuda al OP debería ser bastante pequeño.
Doc Brown
77
@DocBrown Realmente no deberías tratar de evitar a los buenos programadores, pueden ser útiles para tener cerca.
RyanfaeScotland
@RyanfaeScotland: maldición, debería leer mis comentarios dos veces antes de publicarlos (o al menos dentro de los cinco minutos que puedo cambiarlos) ;-)
Doc Brown
6

Nunca he visto una "mejor práctica" documentada con respecto a este tema en particular, pero mi estándar personal es enumerarlos en el orden en que aparecerán en el método para el que se están utilizando o si el método es más adecuado. paso a una capa de datos Los enumeraré en el orden en que aparecerían en el esquema db o en los métodos de la capa de datos.

Además, cuando hay múltiples sobrecargas de un método, noto que la manera típica es enumerarlos comenzando con parámetros que son comunes a todos (o la mayoría) de los métodos con cada método diferente que se agrega al final para cada sobrecarga de método como :

void func1(string param) { }
void func2(string param, int param2) { }
void func3(string param, string param3) { }
void func3(string param, int param2, string param3) { }
Joel Etherton
fuente
6

Orden: entrada (s), salida (s), parámetros opcionales.

Jonathan Khoo
fuente
3

A menudo sigo la convención C / C ++ de colocar constprimero los parámetros (es decir, los parámetros que ingresa por valor) y luego los que ingresa por referencia. Puede que este no sea necesariamente el método correcto de invocar funciones, pero si está interesado en cómo cada compilador maneja los parámetros, eche un vistazo a los siguientes enlaces para conocer las reglas que rigen y / o el orden en que los parámetros se insertan en la pila.

http://msdn.microsoft.com/en-us/library/zthk2dkh%28v=vs.80%29.aspx

http://en.wikipedia.org/wiki/Calling_convention

Cuenta
fuente
Otro enlace en el que puede estar interesado es msdn.microsoft.com/en-us/library/984x0h58%28v=vs.71%29.aspx Para funciones recursivas, eche un vistazo al siguiente enlace. publications.gbdirect.co.uk/c_book/chapter4/… No es que lo haya olvidado, pero como soy un usuario nuevo, no puedo publicar un comentario que tenga más de dos enlaces ... ¡qué frustrante!
Bill
1

Por lo general, simplemente elijo el parámetro "lo que parece menos cíclico". Cuantas menos veces necesite ir a la definición de método / función, mejor. Y es bueno tener parámetros con nombre que son descriptivos de para qué se utilizan, de esa manera cuando aparece la pequeña información sobre herramientas (VS), lo hace aún más fácil.

Si tiene líneas y líneas de parámetros, puede considerar un diseño diferente. Dé un paso atrás y vea cómo puede dividir eso en más funciones / métodos. Es solo una idea, pero cuando tengo una docena de parámetros en mi función, casi siempre no es un problema de parámetros, sino un problema de diseño.


fuente
2
"cyprtic": una excelente manera de hacer el punto.
Bevan
2
¿Nunca has tenido un error tipográfico antes, @Bevan?
1
Tengo errores tipográficos todo el tiempo: escribir cyprtic fue tan bueno que pensé que era a propósito . Vuelva a cambiarlo, ayuda a aclarar su punto, incluso si fue un accidente.
Bevan
1
@Bevan, jaja está bien, veo lo que estás diciendo. ¡Buen punto!
1

A veces (raramente), parece que crear una función que tome una cantidad decente de parámetros es la mejor ruta.

El uso de varios parámetros suele ser un indicador claro de que usted viola el SRP en este método. Es poco probable que un método que necesita muchos parámetros haga una sola cosa. La exclusión puede ser una función matemática o un método de configuración, donde de hecho se necesitan varios parámetros como tales. Evitaría múltiples parámetros como el diablo evita el agua bendita. Cuantos más parámetros use dentro de un método, mayores serán las posibilidades de que el método sea (demasiado) complejo; cuanto más complejidad significa: más difícil de mantener y eso es menos deseable.

Sin embargo, cuando lo hago, siento que a menudo elijo el orden de los parámetros al azar. Por lo general, voy por "orden de importancia", con el parámetro más importante primero.

En principio estás eligiendo al azar . Por supuesto, puede pensar que el parámetro A es más relevante que el parámetro B ; pero ese podría no ser el caso para los usuarios de su API, quienes piensan que B es el parámetro más relevante. Entonces, incluso si estuviera atento al elegir el pedido, para otros podría parecer aleatorio .

¿Hay una mejor manera de hacer esto? ¿Existe una forma de "mejor práctica" de ordenar parámetros que mejore la claridad?

Hay varias salidas:

a) El caso trivial: no utilice más de un parámetro.

b) Como no especificó, qué idioma ha elegido, existe la posibilidad de que elija un idioma con parámetros con nombre . Este es un buen azúcar sintáctico que le permite aflojar la importancia del orden de los parámetros:fn(name:"John Doe", age:36)

No todos los idiomas permiten tales sutilezas. ¿Entonces, qué?

c) Podría usar un Diccionario / Mapa de hash / Matriz asociativa como parámetro: por ejemplo, Javascript permitiría lo siguiente: fn({"name":"John Doe", age:36})que no está lejos de (b).

d) Por supuesto, si trabaja con un lenguaje de tipo estático como Java. podría usar un Hashmap , pero perdería información de tipo (por ejemplo, cuando trabaje con HashMap<String, Object>) cuando los parámetros tengan diferentes tipos (y necesiten convertir).

El siguiente paso lógico sería pasar un Object(si está utilizando Java) con las propiedades apropiadas o algo más liviano como una estructura (si escribe, por ejemplo, C # o C / C ++).

Regla de oro:

1) Mejor caso: su método no necesita ningún parámetro

2) Buen caso: su método necesita un parámetro

3) Caso tolerable: su método necesita dos parámetros

4) Todos los demás casos deben ser refactorizados

Thomas Junk
fuente
0

A menudo, un objeto complejo como parámetro es mejor: una versión pobre de parámetros con nombre que funciona en la mayoría de las plataformas. Y abre la puerta a parámetros con comportamiento para arrancar.

Para ver un ejemplo de la mayoría de las cosas que no debe hacer, puede intentar leer la documentación de la biblioteca estándar de PHP.

Wyatt Barnett
fuente
0

Por lo general, primero los solicito con los requisitos, que mediante alguna medida combinada de importancia y frecuencia de uso de acuerdo con un "sentimiento" (puede verse como ORDER BY required DESC, SOME_MAGIC_FEELING(importancy,frequency)) y no de acuerdo con ninguna práctica específica.

Sin embargo, como otros han señalado, creo que el problema subyacente que hace que esto sea un problema es el uso de demasiados parámetros (en mi humilde opinión, cualquier cosa> 3 es demasiado) y ese es el problema real que debe abordar. Hay una publicación interesante sobre eso en el blog de Rebecca Murphey.

Creo que cuando solo tienes 1-3 argumentos, el orden correcto es bastante obvio y simplemente "sientes" lo que está bien.

shesek
fuente
0

Similar a la respuesta de @Wyatt Barnetts, cualquier cosa más que unos pocos parámetros o parámetros muy explícitos para el método, recomendaría pasar un objeto en su lugar. Esto suele ser más fácil de actualizar / mantener, más claro de leer y elimina la necesidad de preocuparse por ordenar . Además, demasiados parámetros para un método es un olor a código y hay patrones de refactorización comunes que puede seguir para ayudar a corregirlo.

Ejemplo explícito:

public int add(int left, int right)
{
  return left + right;
}

Dado que este es un ejemplo bastante claramente definido y la suma es conmutativa (el orden no importa), simplemente continúe.

Sin embargo, si agrega algo más complejo:

public SomeComplexReturnValue Calculate(int i, float f, string s, object o)
{
  // do work here
}

Se convertiría:

public class SomeComplexInputForCalculation
{
  public int i; 
  public float f;
  public string s; 
  public object o;
}

public SomeComplexReturnValue Calculate(SomeComplexInputForCalculation input)
{
  // do work here
}

Espero que esto ayude...

Longda
fuente
0

Pídelo de la forma que creas que se beneficiará con el curry. Por ejemplo, los parámetros de la función primero.

alternativa
fuente
0

"Importante primero" no significa mucho. Hay algunas convenciones.

Si pasa el objeto de llamada (a menudo llamado remitente), eso va primero.

Debe haber cierta fluidez en la lista, lo que significa que debe saber de qué se trata una discusión para cuando la lea. Ejemplo:

CopyFolder (ruta de cadena, bool recursivo);

Si tuviera que poner primero el recursivo, sería confuso porque todavía no hay contexto. Si ya se trata de copiar (1), una carpeta (2), entonces el argumento recursivo comienza a tener sentido.

Esto también hace que las características similares a IntelliSense funcionen bien: el usuario aprende a medida que completa los argumentos, construye una comprensión del método y lo que hace. Del mismo modo, las sobrecargas deben mantener el mismo orden para las partes que son iguales.

Entonces todavía puede encontrar que hay argumentos que son "iguales" a este respecto y puede sentirse tentado a dejar que el orden dependa del tipo de argumento. Solo porque tener un par de cuerdas seguidas y luego un par de booleanos se ve mejor. En algún nivel, esto se convertirá en un arte más que en una habilidad.

No me importa mucho la afirmación de que debes envolver todos los argumentos en un objeto solo para terminar con un solo argumento. Eso es solo engañarte a ti mismo (no hace que el método sea menos complejo, todavía tiene todos estos argumentos, solo los escondiste). Esto puede ser conveniente hacerlo si pasa el conjunto de un método a otro un par de veces, la refactorización seguramente será mucho más fácil, pero en cuanto al diseño, solo pretende que está haciendo la diferencia. No es como si terminaras con un objeto significativo que represente algo, solo serán un montón de argumentos, no diferentes de la lista en la declaración del método. No hará feliz al tío Bob.

Martin Maat
fuente