Contexto
Recientemente me interesé en producir un código mejor formateado. Y por mejor quiero decir "seguir las reglas respaldadas por suficientes personas para considerarlo una buena práctica" (ya que, por supuesto, nunca habrá una "mejor" forma única de codificar).
En estos días, codifico principalmente en Ruby, así que comencé a usar un linter (Rubocop) para proporcionarme información sobre la "calidad" de mi código (esta "calidad" está definida por la guía de estilo ruby del proyecto impulsado por la comunidad ).
Tenga en cuenta que usaré "calidad" como en "calidad del formateo", no tanto sobre la eficiencia del código, incluso si en algunos casos, la eficiencia del código en realidad se ve afectada por la forma en que se escribe el código.
De todos modos, al hacer todo eso, me di cuenta (o al menos recordé) algunas cosas:
- Algunos lenguajes (más notablemente Python, Ruby y otros) permiten crear código de líneas geniales.
- Seguir algunas pautas para su código puede hacerlo significativamente más corto y aún así muy claro
- Sin embargo, seguir estas pautas demasiado estrictamente puede hacer que el código sea menos claro / fácil de leer.
- El código puede respetar algunas pautas casi a la perfección y aún ser de baja calidad.
- La legibilidad del código es principalmente subjetiva (como en "lo que encuentro claro podría ser completamente oscuro para un desarrollador desarrollador")
Esas son solo observaciones, no reglas absolutas, por supuesto. También notará que la legibilidad del código y las siguientes pautas pueden parecer ajenas en este punto, pero aquí las pautas son una forma de reducir la cantidad de formas de reescribir un fragmento de código.
Ahora, algunos ejemplos, para aclarar todo eso.
Ejemplos
Tomemos un caso de uso simple: tenemos una aplicación con un " User
" modelo. Un usuario tiene una dirección opcional firstname
y surname
obligatoria email
.
Quiero escribir un método " name
" que devolverá entonces el nombre ( firstname + surname
) del usuario si al menos su firstname
o surname
está presente, o su email
como un valor de retorno en caso contrario.
También quiero que este método tome un " use_email
" como parámetro (booleano), lo que permite utilizar el correo electrónico del usuario como valor de reserva. Este " use_email
" parámetro debe ser predeterminado (si no se pasa) como " true
".
La forma más simple de escribir eso, en Ruby, sería:
def name(use_email = true)
# If firstname and surname are both blank (empty string or undefined)
# and we can use the email...
if (firstname.blank? && surname.blank?) && use_email
# ... then, return the email
return email
else
# ... else, concatenate the firstname and surname...
name = "#{firstname} #{surname}"
# ... and return the result striped from leading and trailing spaces
return name.strip
end
end
Este código es la forma más simple y fácil de entender. Incluso para alguien que no "habla" Ruby.
Ahora intentemos hacerlo más corto:
def name(use_email = true)
# 'if' condition is used as a guard clause instead of a conditional block
return email if (firstname.blank? && surname.blank?) && use_email
# Use of 'return' makes 'else' useless anyway
name = "#{firstname} #{surname}"
return name.strip
end
Esto es más corto, aún fácil de entender, si no más fácil (la cláusula de protección es más natural de leer que un bloque condicional). La cláusula de protección también lo hace más compatible con las pautas que estoy usando, así que gane-gane aquí. También reducimos el nivel de sangría.
Ahora usemos algo de magia Ruby para hacerlo aún más corto:
def name(use_email = true)
return email if (firstname.blank? && surname.blank?) && use_email
# Ruby can return the last called value, making 'return' useless
# and we can apply strip directly to our string, no need to store it
"#{firstname} #{surname}".strip
end
Incluso más corto y siguiendo las pautas perfectamente ... pero mucho menos claro ya que la falta de declaración de devolución hace que sea un poco confuso para aquellos que no están familiarizados con esta práctica.
Es aquí donde podemos comenzar a hacer la pregunta: ¿realmente vale la pena? Deberíamos decir "no, hacerlo legible y agregar ' return
'" (sabiendo que esto no respetará las pautas). ¿O deberíamos decir "Está bien, es la forma de Ruby, aprende el maldito idioma"?
Si tomamos la opción B, ¿por qué no hacerlo aún más corto?
def name(use_email = true)
(email if (firstname.blank? && surname.blank?) && use_email) || "#{firstname} #{surname}".strip
end
Aquí está, el one-liner! Por supuesto, es más corto ... aquí aprovechamos el hecho de que Ruby devolverá un valor u otro dependiendo de cuál esté definido (ya que el correo electrónico se definirá en las mismas condiciones que antes).
También podemos escribirlo:
def name(use_email = true)
(email if [firstname, surname].all?(&:blank?) && use_email) || "#{firstname} #{surname}".strip
end
Es breve, no es tan difícil de leer (quiero decir, todos hemos visto lo que puede parecer un trazador de líneas feo), bueno Ruby, cumple con la directriz que uso ... Pero aún así, en comparación con la primera forma de escribir es mucho menos fácil de leer y entender. También podemos argumentar que esta línea es demasiado larga (más de 80 caracteres).
Pregunta
Algunos ejemplos de código pueden mostrar que elegir entre un código de "tamaño completo" y muchas de sus versiones reducidas (hasta el famoso one-liner) puede ser difícil ya que, como podemos ver, los one-liners no pueden ser tan temibles, pero aún así, nada superará el código de "tamaño completo" en términos de legibilidad ...
Así que aquí está la verdadera pregunta: ¿dónde parar? ¿Cuándo es corto, lo suficientemente corto? ¿Cómo saber cuándo el código se vuelve "demasiado corto" y menos legible (teniendo en cuenta que es bastante subjetivo)? Y aún más: ¿cómo codificar siempre en consecuencia y evitar mezclar frases con trozos de código "de tamaño completo" cuando me da la gana?
TL; DR
La pregunta principal aquí es: cuando se trata de elegir entre un "fragmento de código largo pero claro, legible y comprensible" y un "poderoso, más corto pero más difícil de leer / entender", sabiendo que esos dos son los mejores y los mejores. fondo de una escala y no las dos únicas opciones: ¿cómo definir dónde está la frontera entre "lo suficientemente claro" y "no tan claro como debería ser"?
La pregunta principal no es la clásica "One-liners vs. legibilidad: ¿cuál es mejor?" pero "¿Cómo encontrar el equilibrio entre esos dos?"
Editar 1
Los comentarios en los ejemplos de código están destinados a ser "ignorados", están aquí para aclarar lo que está sucediendo, pero no deben tenerse en cuenta al evaluar la legibilidad del código.
return
palabra clave agregada . Esos siete personajes agregan bastante claridad en mis ojos.
[firstname,surname,!use_email].all?(&:blank?) ? email : "#{firstname} #{surname}".strip
... porque false.blank?
devuelve verdadero y el operador ternario te ahorra algunos caracteres ... ¯ \ _ (ツ) _ / ¯
return
supone que debe agregar la palabra clave? No proporciona información alguna . Es puro desorden.