En aras de la legibilidad, a menudo me encuentro definiendo variables temporales mientras invoco funciones, como el siguiente código
var preventUndo = true;
doSomething(preventUndo);
La versión más corta de esto a esto sería,
doSomething(true);
Pero cuando vuelvo al código, a menudo me pregunto a qué se true
refiere. ¿Existe una convención para este tipo de acertijo?
coding-style
Duopixel
fuente
fuente
doSomething( Undo.PREVENT )
Undo = { PREVENT = true, DONT_PREVENT = false }
. Pero en JavaScript, la convención es hacerlo así:function myFunction( mandatoryArg1, mandatoryArg2, otherArgs ) { /*...*/ }
y luegomyFunction( 1, 2, { option1: true, option2: false } )
.doSomething(preventUndo=True)
Respuestas:
Explicando las variables
Su caso es un ejemplo de la introducción que explica la refactorización variable . En resumen, una variable explicativa es una que no es estrictamente necesaria, pero le permite dar un nombre claro a algo, con el objetivo de aumentar la legibilidad.
El código de buena calidad comunica la intención al lector; y como desarrollador profesional, la legibilidad y la mantenibilidad son sus objetivos número 1.
Como tal, la regla general que recomendaría es esta: si el propósito de su parámetro no es inmediatamente obvio, no dude en usar una variable para darle un buen nombre. Creo que esta es una buena práctica en general (a menos que se abuse de ella). Aquí hay un ejemplo rápido y artificial: considere:
versus el un poco más largo, pero posiblemente más claro:
Parámetros booleanos
Su ejemplo realmente resalta por qué los booleanos en las API a menudo son una mala idea : por el lado de la llamada, no hacen nada para explicar lo que está sucediendo. Considerar:
Tendría que buscar qué significan esos parámetros; si fueran enumeraciones, sería mucho más claro:
Editar:
Se agregaron encabezados e intercambiaron el orden de los dos párrafos principales, porque mucha gente se estaba enfocando en la parte de los parámetros booleanos (para ser justos, originalmente era el primer párrafo). También se agregó un ejemplo a la primera parte.
fuente
doSomething(preventUndo: true);
es lo suficientemente claro. Además, ¿crear una enumeración para cada booleano en su API? ¿Seriamente?No escriba código que no necesita.
Si le resulta
doSomething(true)
difícil de entender, debe agregar un comentario:o, si el idioma lo admite, agregue el nombre del parámetro:
De lo contrario, confíe en su IDE para obtener la firma del método llamado:
Casos donde es útil
Poner una variable adicional puede ser útil:
Para fines de depuración:
Se puede escribir el mismo código en una sola línea, pero si desea poner un punto de interrupción antes de agregar un producto al carrito para ver si la identificación del producto es correcta, escribir dos líneas en lugar de una es una buena idea.
Si tiene un método con muchos parámetros y cada parámetro parte de una evaluación. En una línea, esto puede volverse totalmente ilegible.
Si la evaluación de un parámetro es demasiado complicada. Un ejemplo de un código que he visto:
¿Por qué no en otros casos?
¿Por qué no deberías crear variables adicionales en casos simples?
No por el impacto en el rendimiento. Esta sería una suposición muy errónea de un desarrollador principiante que está micro optimizando su aplicación. No hay impacto en el rendimiento en la mayoría de los idiomas, ya que el compilador incorporará la variable. En aquellos idiomas en los que el compilador no hace eso, puede ganar unos pocos microsegundos al incluirlo a mano, lo que no vale la pena. No hagas eso.
Pero debido al riesgo de disociar el nombre que le da a la variable al nombre del parámetro.
Ejemplo:
Digamos que el código original es:
Más tarde, un desarrollador que trabaja en
doSomething
avisos de que recientemente, el historial de deshacer introdujo dos nuevos métodos:Ahora,
preventUndo
parece no muy claro. ¿Significa que solo se evitará la última acción? ¿O tal vez significa que el usuario ya no podrá usar la función de deshacer? ¿O que se borrará el historial de deshacer? Los nombres más claros serían:Entonces ahora tienes:
¿Qué hay de las enumeraciones?
Algunas otras personas sugirieron usar enumeraciones. Si bien resuelve el problema inmediato, crea uno más grande. Vamos a ver:
Problemas con eso:
Es demasiado código.
if (preventUndo == undoPrevention.prevent)
? ¡¿Seriamente?! No quiero escribir talif
s cada vez.Agregar un elemento a la enumeración es muy tentador más adelante, si estoy usando la misma enumeración en otro lugar. ¿Qué pasa si lo modifico así:
¿Que pasará ahora? ¿El
doSomething
método funcionará como se espera? Para evitar esto, sería necesario escribir este método de esta manera desde el principio:¡La variante que usa booleanos comienza a verse muy bien!
fuente
preventUndo
aallowUndo
la firma ...Tampoco deberías escribir métodos con banderas booleanas.
Para citar a Robert C. Martin, dice en su libro Clean Code (ISBN-13 978-0-13-235088-4), "Pasar un booleano a una función es una práctica verdaderamente terrible".
Para parafrasearlo, el razonamiento es que el hecho de que tenga un interruptor verdadero / falso significa que su método probablemente esté haciendo dos cosas diferentes (es decir, "Hacer algo con deshacer" y "Hacer algo sin deshacer"), y por lo tanto debe ser dividido en dos métodos diferentes (que internamente pueden llamar lo mismo).
fuente
doSomethingUndoable()
podría capturar los cambios y luego llamar adoSomething()
Cuando observa dos clases para ver qué tan acopladas están, una de las categorías es el acoplamiento de datos , que se refiere al código en una clase llamando a los métodos de otra clase y simplemente pasando datos, como para qué mes desea un informe y otro La categoría es el acoplamiento de control , llamar a métodos y pasar algo que controla el comportamiento del método. El ejemplo que uso en clase es una
verbose
bandera o unareportType
bandera, peropreventUndo
también es un gran ejemplo. Como acaba de demostrar, el acoplamiento de control dificulta la lectura del código de llamada y la comprensión de lo que sucede. También hace que el código de llamada sea vulnerable a los cambiosdoSomething()
que usan el mismo indicador para controlar tanto deshacer como archivar, o agregar otro parámetro adoSomething()
para controlar el archivado, rompiendo así su código, etc.El problema es que el código está muy bien acoplado. Pasar un bool para controlar el comportamiento es, en mi opinión, una señal de una mala API. Si posee la API, cámbiela. Dos métodos,
doSomething()
ydoSomethingWithUndo()
, sería mejor. si no lo posee, escriba dos métodos de envoltura usted mismo en el código que sí posee, y haga que uno de ellos llamedoSomething(true)
y el otro llamedoSomething(false)
.fuente
No es el rol del llamante definir el rol de los argumentos. Siempre busco la versión en línea y compruebo la firma de la función llamada si tengo dudas.
La variable debe nombrarse después de su rol en el alcance actual. No El alcance donde se envían.
fuente
Lo que haría en JavaScript es que la función tome un objeto como el único parámetro similar a este:
Luego llámalo con:
fuente
doSomething(x)
método! jaja ... Ni siquiera me di cuenta de tu publicación hasta ahora.Soy aficionado a aprovechar las características del lenguaje para ayudarme a aclararme. Por ejemplo, en C # puede especificar parámetros por nombre:
En JavaScript, generalmente prefiero usar un objeto de opciones como argumento por este motivo:
Sin embargo, esa es solo mi preferencia. Supongo que dependerá en gran medida del método que se llame y de cómo el IDE ayuda al desarrollador a comprender la firma.
fuente
$.ajax({url:'', data:''})
etc., etc.Creo que este es el más simple y fácil de leer:
A MainMa no le gusta eso. Es posible que tengamos que estar de acuerdo en no estar de acuerdo, o podemos usar una solución que Joshua Bloch propone:
Ahora, si alguna vez agrega un Undo.LIMITED_UNDO, su código no se compilará a menos que implemente el método createUndoBuffer (). Y en doSomething () no hay if (Undo.ALLOW == u). Lo hice en ambos sentidos y el segundo método es bastante pesado y un poco difícil de entender cuando la enumeración Deshacer se expande a páginas y páginas de código, pero te hace pensar. Por lo general, me quedo con el método más simple para reemplazar un booleano simple con una enumeración de 2 valores hasta que tenga razones para cambiar. Cuando agrego un tercer valor, utilizo mi IDE para "Buscar usos" y arreglar todo.
fuente
Creo que su solución hace que su código sea un poco más legible, pero evitaría definir una variable adicional solo para que su función llame más claramente. Además, si desea utilizar una variable adicional, la marcaría como constante si su lenguaje de programación lo admite.
Se me ocurren dos alternativas que no implican una variable adicional:
1. Use un comentario extra
2. Use una enumeración de dos valores en lugar de boolean
Puede usar la alternativa 2 si su idioma admite enumeraciones.
EDITAR
Por supuesto, usar argumentos con nombre también es una opción, si su idioma los admite.
En cuanto a la codificación adicional que necesitan las enumeraciones, realmente me parece insignificante. En lugar de
tienes
o
E incluso si se trata de escribir un poco más, recuerde que el código se escribe una vez y se lee muchas veces, por lo que puede valer la pena escribir un poco más ahora para encontrar un código más legible seis meses después.
fuente
Estoy de acuerdo con lo que dijo @GlenPeterson:
pero también interesante sería el siguiente, ya que me parece que solo hay dos posibilidades (verdadero o falso)
fuente
doSomething
pero que le permita a la persona que llama elegir si desea deshacer. Una solución puede ser tener una sobrecarga que tome la opción booleana, pero también tener versiones de envoltura que llamen a la versión anterior con el parámetro siempre verdadero o siempre falso.Como está preguntando sobre JavaScript principalmente, al usar coffeescript puede tener un argumento con nombre como sintaxis, súper fácil:
compila a
Diviértete en http://js2coffee.org/ para probar las posibilidades
fuente
Solo para una solución menos convencional y dado que el OP mencionó Javascript, una posibilidad es usar una matriz asociativa para asignar valores. La ventaja sobre un solo booleano es que puede tener tantos argumentos como desee y no preocuparse por su secuencia.
Me doy cuenta de que esto es un reflejo de alguien de un fondo fuertemente tipado y puede que no sea tan práctico, pero considere esto por originalidad.
Otra posibilidad es tener un objeto y también es posible en Javascript. No estoy 100% seguro de que esto sea sintácticamente correcto. Eche un vistazo a patrones como Factory para obtener más inspiración.
fuente
doSomethingOptions.PREVENTUNDO
) en lugar de la notación de acceso a la matriz.El enfoque de su pregunta y de las otras respuestas es mejorar la legibilidad donde se llama la función, que es un enfoque con el que estoy de acuerdo. Cualquier directriz específica, evento "sin argumentos booleanos", siempre debe funcionar como un medio para este fin y no convertirse y terminar ellos mismos.
Creo que es útil tener en cuenta que este problema se evita principalmente cuando el lenguaje de programación admite argumentos con nombre / argumentos de palabras clave, como en C # 4 y Python, o cuando los argumentos del método están intercalados en el nombre del método, como en Smalltalk o Objective-C.
Ejemplos:
fuente
Se debe evitar pasar variables booleanas como argumentos a las funciones. Porque las funciones deberían hacer una cosa a la vez. Al pasar la variable booleana, la función tiene dos comportamientos. Esto también crea un problema de legibilidad para una etapa posterior o para otros programadores que tienden a ver su código. Esto también crea un problema al probar la función. Probablemente en este caso tienes que crear dos casos de prueba. Imagine que tiene una función que tiene una declaración de cambio y cambia su comportamiento en función del tipo de cambio, entonces debe tener tantos casos de prueba diferentes definidos para él.
Cada vez que uno tiene un argumento booleano para la función, tiene que modificar el código para que no escriba ningún argumento booleano.
fuente
if(param) {...} else {...}
)?Un buen compilador para lenguajes de bajo nivel como C ++ optimizará el código de máquina de más de 2 líneas de la siguiente manera:
por lo que no importa en el rendimiento, pero aumentará la legibilidad.
También es útil usar una variable en caso de que se convierta en variable más adelante y también pueda aceptar otros valores.
fuente