Escribir código elegante y legible
En este tutorial, le daremos nueve técnicas prácticas para escribir código elegante y legible. No hablaremos de arquitecturas, lenguajes o plataformas específicas. El foco está en escribir un mejor código. Empecemos.
“Medir el progreso de la programación por líneas de código es como medir el progreso de la construcción de aeronaves por peso”. – Bill Gates
Introducción
Si usted es un desarrollador, entonces probablemente haya habido ocasiones en que ha escrito código y, después de unos días, semanas o meses, lo miró y se dijo: “¿Qué hace este código?” La respuesta a esa pregunta podría haber sido “¡Realmente no lo sé!” En ese caso, lo único que puede hacer es revisar el código de principio a fin, tratando de comprender lo que estaba pensando cuando lo escribió.
Esto ocurre principalmente cuando somos flojos y solo queremos implementar esa nueva característica que solicitó el cliente. Solo queremos hacer el trabajo con el menor esfuerzo posible. Y cuando funciona, no nos importa el código en sí, porque el cliente nunca verá la fea verdad, y mucho menos la entenderá. ¿Derecho? Incorrecto. En estos días, colaborar en el software se ha convertido en el valor predeterminado y las personas verán, leerán e inspeccionarán el código que escribes. Incluso si su código no es examinado por sus colegas, debe acostumbrarse a escribir código claro y legible. Siempre.
La mayoría de las veces, no trabajas solo en un proyecto. Frecuentemente vemos código feo con variables que tienen nombres como i, a, p, pro y rqs. Y si realmente se pone mal, este patrón es visible en todo el proyecto. Si esto le suena familiar, entonces estoy bastante seguro de que se ha hecho la pregunta “¿Cómo puede esta persona escribir código como este?” Por supuesto, esto lo hace aún más agradecido cuando se encuentra con un código claro, legible e incluso hermoso. El código claro y limpio se puede leer en segundos y puede ahorrarle mucho tiempo a usted y a sus colegas. Esa debería ser tu motivación para escribir código de calidad.
- ¿En qué tecnología se basa eBay?
- ¿Qué tan importante es el inglés para los programadores?
- ¿Cuáles son las herramientas necesarias para desarrollar un producto integrado, especialmente software?
- Lo importante en la creación de software: la calidad del código o el resultado final del producto.
- ¿Qué se discute durante una reunión de Scrum?
1. Fácil de entender
Todos estamos de acuerdo en que el código debe ser fácil de entender. ¿Derecho? El primer ejemplo se centra en el espaciado. Veamos dos ejemplos.
volver género == “1”? peso * (altura / 10): peso * (altura * 10);
if (género == “1”) {
peso de retorno * (altura / 10);
} más {
peso de retorno * (altura * 10);
}
Aunque el resultado de estos ejemplos es idéntico, se ven bastante diferentes. ¿Por qué debería usar más líneas de código si puede escribir menos? Exploremos otros dos ejemplos, algo que apuesto a que ves con frecuencia.
for (Node * node = list-> head; node! = NULL; node = node-> next)
imprimir (nodo-> datos);
Nodo * nodo = lista-> cabeza;
if (nodo == NULL) devuelve;
while (nodo-> siguiente! = NULL) {
Imprimir (nodo-> datos);
nodo = nodo-> siguiente;
}
if (nodo! = NULL) Imprimir (nodo-> datos);
Nuevamente, el resultado de estos ejemplos es idéntico. ¿Cuál es mejor? ¿Y por qué? ¿Menos líneas de código significan mejor código? Revisaremos esta pregunta más adelante en este tutorial.
2. ¿Más pequeño siempre es mejor?
En informática, a menudo escuchas la frase “menos es más”. En términos generales, si puede resolver un problema en menos líneas de código, mejor. Probablemente le llevará menos tiempo comprender una clase de 200 líneas que una clase de 500 líneas. Sin embargo, ¿es esto siempre cierto? Eche un vistazo a los siguientes ejemplos.
reserva ((! room = FindRoom (room_id))) || ! room-> isOccupied ());
room = FindRoom (room_id);
if (habitación! = NULL)
reserva (! room-> isOccupied ());
¿No está de acuerdo con que el segundo ejemplo es más fácil de leer y entender? Debe poder optimizar la legibilidad. Por supuesto, podría agregar algunos comentarios al primer ejemplo para que sea más fácil de entender, pero ¿no es mejor omitir los comentarios y escribir código que sea más fácil de leer y entender?
// Determina dónde generar el monstruo a lo largo del eje Y
CGSize winSize = [CCDirector sharedDirector] .winSize;
int minY = monster.contentSize.width / 2;
int maxY = winSize.width – monster.contentSize.width / 2;
int rangeY = maxY – minY;
int actualY = (arc4random ()% rangeY) + minY;
3. Nombramiento
Elegir nombres descriptivos para cosas como variables y funciones es un aspecto clave de la escritura de código legible. Ayuda tanto a sus colegas como a usted a comprender rápidamente el código. Nombrar una variable tmp no le dice nada más que que la variable es temporal por alguna razón, que no es más que una suposición educada. No dice si la variable almacena un nombre, una fecha, etc.
Otro buen ejemplo es nombrar un método stop. No es un mal nombre per se, pero eso realmente depende de la implementación del método. Si realiza una operación peligrosa que no se puede deshacer, es posible que desee cambiar el nombre para matar o pausar si la operación se puede reanudar. ¿Entiendes la idea?
Si está trabajando con una variable para el peso de las papas, ¿por qué lo llamaría tmp? Cuando vuelva a visitar ese fragmento de código unos días después, no recordará para qué se utiliza tmp.
No estamos diciendo que tmp es un mal nombre para una variable, porque a veces tmp es perfectamente razonable como nombre de variable. Eche un vistazo al siguiente ejemplo en el que tmp no es una mala elección en absoluto.
tmp = primera_patata;
first_potato = second_potato;
segunda_patata = tmp;
En el ejemplo anterior, tmp describe lo que hace, almacena temporalmente un valor. No se pasa a una función o método, y no se incrementa o modifica. Tiene una vida útil bien definida y ningún desarrollador experimentado se verá afectado por el nombre de la variable. A veces, sin embargo, es simplemente pereza. Echa un vistazo al siguiente ejemplo.
NSString * tmp = Página en user.name ;
tmp + = “” + user.phone_number;
tmp + = “” + user.email;
…
[plantilla setObject: tmp forKey: @ “user_info”];
Si tmp almacena la información del usuario, ¿por qué no se llama userInfo? El nombre apropiado de variables, funciones, métodos, clases, etc. es importante cuando se escribe código legible. No solo hace que su código sea más legible, sino que también le ahorrará tiempo en el futuro.
Objective-C es bastante detallado, pero es muy fácil de leer. Apple utiliza una convención de nomenclatura bien definida que puede adoptar en la mayoría de los lenguajes de programación. Puede leer más sobre esta convención de nomenclatura en Programación con Objective-C.
4. Agregar significado a los nombres
Como vimos en el consejo anterior, es importante elegir los nombres sabiamente. Sin embargo, es igualmente importante agregar significado a los nombres que usa para variables, funciones, métodos, etc. Esto no solo ayuda a evitar confusiones, sino que hace que el código que escribe sea más fácil de entender. Elegir un nombre que tenga sentido es casi como agregar metadatos a una variable o método. Elija nombres descriptivos y evite los genéricos. La palabra agregar, por ejemplo, no siempre es ideal, como puede ver en el siguiente ejemplo.
bool addUser (Usuario u) {
…
}
No está claro qué se supone que debe hacer addUser. ¿Agrega un usuario a una lista de usuarios, a una base de datos o a una lista de personas invitadas a una fiesta? Compare esto con registerUser o signupUser. Esto tiene más sentido. ¿Derecho? Eche un vistazo a la siguiente lista para tener una mejor idea de a qué nos dirigimos.
Los sinónimos de las palabras hacen, realizan, ejecutan, componen, agregan, inician, inician, crean, comienzan, abren, explotan, detonan, explotan, detienen, explotan
5. Tamaño del nombre
A muchos programadores no les gustan los nombres largos, porque son difíciles de recordar y engorrosos de escribir. Por supuesto, un nombre no debe ser ridículamente largo como newClassForNavigationControllerNamedFirstViewController. Esto es difícil de recordar y simplemente hace que su código sea feo e ilegible.
Como vimos anteriormente, los nombres cortos opuestos tampoco son buenos. ¿Cuál es el tamaño correcto para una variable o nombre de método? ¿Cómo decides entre nombrar una variable len, length o user_name_length? La respuesta depende del contexto y la entidad a la que está vinculado el nombre.
Los nombres largos ya no son un problema cuando se utiliza un IDE (Entorno de desarrollo integrado) moderno. La finalización del código le ayuda a evitar errores tipográficos y también hace sugerencias para que recordar nombres sea menos complicado.
Puede usar nombres cortos (er) si la variable es local. Además, se recomienda usar nombres más cortos para las variables locales para mantener su código legible. Eche un vistazo al siguiente ejemplo.
NSString * link = [[NSString alloc] initWithFormat: @ “ Página en localhost: 8080 “, idCode];
NSURL * infoCode = [NSURL URLWithString: enlace];
6. Nombrar booleanos
Los booleanos pueden ser difíciles de nombrar, ya que pueden tener un significado diferente dependiendo de la forma en que lea o interprete el nombre. En el siguiente fragmento de código, read_password puede significar que el programa ha leído la contraseña, pero también puede significar que el programa debería leer la contraseña.
BOOL readPassword = YES;
Para evitar este problema, puede cambiar el nombre del booleano anterior a didReadPassword para indicar que se ha leído la contraseña o shouldReadPassword para mostrar que el programa necesita leer la contraseña. Esto es algo que ves mucho en Objective-C, por ejemplo.
7. Para comentar o no comentar
Agregar comentarios al código es importante, pero es igualmente importante usarlos con moderación. Deben usarse para ayudar a alguien a comprender su código. Sin embargo, leer comentarios también lleva tiempo y si un comentario no agrega mucho valor, entonces se pierde ese tiempo. El siguiente fragmento de código muestra cómo no usar comentarios.
// Esto sucede cuando se recibe una advertencia de memoria
– (nulo) didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Deseche cualquier recurso que pueda recrearse.
}
// Esto valida los campos
– (BOOL) validateFields {
}
¿Son útiles estos fragmentos de código? La respuesta es probablemente no.” Los comentarios en los ejemplos anteriores no agregan información adicional, especialmente porque los nombres de los métodos ya son muy descriptivos, lo cual es común en Objective-C. No agregue comentarios que expliquen lo obvio. Echa un vistazo al siguiente ejemplo. ¿No es este un mejor uso de los comentarios?
// Determina la velocidad del monstruo
int minDuration = 2.0;
int maxDuration = 8.0;
int rangeDuration = maxDuration – minDuration;
int actualDuration = (arc4random ()% rangeDuration) + minDuration;
Comentarios como este hacen que sea muy fácil navegar por una base de código de manera rápida y eficiente. Le ahorra tener que leer el código y le ayuda a comprender la lógica o el algoritmo.
8. Estilo y consistencia
Cada idioma o plataforma tiene una guía de estilo (o más) e incluso la mayoría de las empresas tienen una. ¿Pones las llaves de un método Objective-C en una línea separada o no?
– (nulo) CalculateOffset {
}
– (nulo) CalculateOffset
{
}
La respuesta es que no importa. No hay una respuesta correcta. Por supuesto, hay guías de estilo que puede adoptar. Lo importante es que su código sea consistente en términos de estilo. Aunque esto puede no afectar la calidad de su código, ciertamente afecta la legibilidad y lo más probable es que moleste a sus colegas o a quien lea su código. Para la mayoría de los desarrolladores, el código feo es el peor tipo de código.
Anuncio
9. Métodos enfocados y funciones
Un error común entre los desarrolladores es tratar de incluir tanta funcionalidad en funciones y métodos. Esto funciona, pero es poco elegante y hace que la depuración sea un dolor en el cuello. Su vida, y la de sus colegas, se volverá mucho más fácil si divide problemas más grandes en partes pequeñas y las aborda en funciones o métodos separados. Eche un vistazo al siguiente ejemplo en el que escribimos una imagen en el disco. Esto parece una tarea trivial, pero hay mucho más si quieres hacerlo bien.
– (BOOL) saveToImage: (UIImage *) imagen con FileName: (NSString *) fileName {
Resultado BOOL = NO;
NSString * documentos = nulo;
NSArray * caminos = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
if (paths.count) {
documentos = [rutas objectAtIndex: 0];
NSString * basePath = [documentos stringByAppendingPathComponent: @ “Archive”];
if (! [[NSFileManager defaultManager] fileExistsAtPath: basePath]) {
NSError * error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath: basePath withIntermediateDirectories: YES atributos: nil error: & error];
if (! error) {
NSString * filePath = [basePath stringByAppendingPathComponent: fileName];
resultado = [UIImageJPEGRepresentation (imagen, 8.0) writeToFile: filePath atómicamente: SÍ];
} más {
NSLog (@ “No se puede crear el directorio debido al error% @ con la información del usuario% @.”, Error, error.userInfo);
}
}
}
resultado de retorno;
}
Si una unidad de código intenta hacer demasiado, a menudo terminas con declaraciones condicionales profundamente anidadas, una gran cantidad de verificación de errores y declaraciones condicionales demasiado complejas. Este método hace tres cosas, busca la ruta del directorio de documentos de la aplicación, busca y crea la ruta para el directorio de archivos y escribe la imagen en el disco. Cada tarea se puede poner en su propio método como se muestra a continuación.
– (BOOL) saveToImage: (UIImage *) imagen con FileName: (NSString *) fileName {
NSString * archivesDirectory = [self applicationArchivesDirectory];
if (! archivesDirectory) devuelve NO;
// Crear ruta
NSString * filePath = [archivesDirectory stringByAppendingPathComponent: fileName];
// Escribir imagen en disco
return [UIImageJPEGRepresentation (imagen, 8.0) writeToFile: filePath atómicamente: SÍ];
}
– (NSString *) applicationDocumentsDirectory {
NSArray * caminos = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
rutas de retorno cuenta? [rutas objectAtIndex: 0]: nil;
}
– (NSString *) applicationArchivesDirectory {
NSString * documentsDirectory = [self applicationDocumentsDirectory];
NSString * archivesDirectory = [documentsDirectory stringByAppendingPathComponent: @ “Archives”];
NSFileManager * fm = [NSFileManager defaultManager];
if (! [fm fileExistsAtPath: archivesDirectory]) {
NSError * error = nil;
[fm createDirectoryAtPath: archivesDirectory withIntermediateDirectories: YES atributos: nil error: & error];
if (error) {
NSLog (@ “No se puede crear el directorio debido al error% @ con la información del usuario% @.”, Error, error.userInfo);
volver nulo
}
}
volver archivesDirectory;
}
Esto es mucho más fácil de depurar y mantener. Incluso puede reutilizar el método applicationDocumentsDirectory en otros lugares del proyecto, que es otro beneficio de dividir problemas más grandes en partes manejables. Probar el código se vuelve mucho más fácil también.
Conclusión
En este artículo, hemos examinado más de cerca la escritura de código legible al elegir sabiamente nombres para variables, funciones y métodos, ser coherentes al escribir código y dividir problemas complejos en fragmentos manejables.
Fuente:
Escribir código elegante y legible – Artículo de Tuts + Code