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 enfoque está en escribir 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 aviones por peso". - Bill Gates

Introducción

Si eres un desarrollador, es probable que haya habido ocasiones en las que hayas escrito un código y, después de unos días, semanas o meses, lo miraste y te dijiste "¿Qué hace esta pieza de 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 entender lo que estaba pensando cuando lo escribió..

Esto ocurre principalmente cuando somos perezosos y solo queremos implementar esa nueva característica que el cliente solicitó. 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á el código feo La verdad, y mucho menos entenderlo. ¿Derecha? 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 usted escribe. Incluso si su código no es examinado por sus colegas, debería convertirlo en un hábito de escribir un código claro y legible. Siempre.

La mayoría de las veces, no trabajas solo en un proyecto. Con frecuencia vemos código feo con variables que tienen nombres como yo, una, pag, Pro, y rqs. Y si realmente se pone malo, este patrón es visible en todo el proyecto. Si esto suena familiar, entonces estoy bastante seguro de que te has preguntado: "¿Cómo puede esta persona escribir un código como este?" Por supuesto, esto te hace más agradecido cuando te encuentras con un código claro, legible e incluso hermoso. El código claro y limpio se puede leer en segundos y puede ahorrarle a usted y a sus colegas mucho tiempo. Esa debe ser tu motivación para escribir código de calidad..

1. Fácil de entender

Todos estamos de acuerdo en que el código debe ser fácil de entender. ¿Derecha? El primer ejemplo se centra en el espaciado. Veamos dos ejemplos..

 devuelve género == "1"? peso * (altura / 10): peso * (altura * 10);
 if (gender == "1") return peso * (altura / 10);  else retorno de peso * (altura * 10); 

Aunque el resultado de estos ejemplos es idéntico, se ven muy 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 frecuentemente.

 para (Nodo * nodo = lista-> cabecera; nodo! = NULL; nodo = nodo-> siguiente) imprimir (nodo-> datos);
 Nodo * nodo = lista-> cabeza; if (node ​​== NULL) devuelve; while (node-> next! = NULL) Imprimir (node-> data); nodo = nodo-> siguiente;  if (node! = NULL) Imprimir (node-> data);

De nuevo, 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? Volveremos a examinar esta pregunta más adelante en este tutorial..

2. Es más pequeño siempre 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 entender una clase de 200 líneas que una clase de 500 líneas. Sin embargo, ¿es esto siempre cierto? Echa un vistazo a los siguientes ejemplos.

 reserva ((! room = FindRoom (room_id))) || ! habitación-> isOccupied ());
 room = FindRoom (room_id); if (room! = NULL) reserva (! room-> isOccupied ());

¿No está de acuerdo en que el segundo ejemplo es más fácil de leer y entender?? Necesitas poder optimizar para facilitar la lectura.. Por supuesto, puede agregar algunos comentarios al primer ejemplo para que sea más fácil de entender, pero ¿no es mejor omitir los comentarios y escribir un código que sea más fácil de leer y entender??

 // Determine 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. nombrar

Elegir nombres descriptivos para cosas como variables y funciones es un aspecto clave para escribir código legible. Ayuda tanto a tus colegas como a ti mismo 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 indica si la variable almacena un nombre, una fecha, etc..

Otro buen ejemplo es nombrar un método detener. No es un mal nombre en sí mismo, 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 cambiarle el nombre a matar o pausa Si la operación puede ser reanudada. ¿Entiendes la idea??

Si está trabajando con una variable para el peso de las papas, ¿por qué la llama? tmp? Cuando vuelva a visitar ese fragmento de código unos días después, no recordará qué tmp se utiliza para.

No estamos diciendo eso tmp es un mal nombre para una variable, porque hay momentos en que 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 = primer_potato; first_potato = second_potato; second_potato = 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 será rechazado por el nombre de la variable. A veces, sin embargo, es simplemente la pereza. Echa un vistazo al siguiente ejemplo..

 NSString * tmp = nombre.usuario; tmp + = "" + user.phone_number; tmp + = "" + user.email;… [template setObject: tmp forKey: @ "user_info"];

Si tmp almacena la información del usuario, ¿por qué no se nombra? Información de usuario? El nombre correcto de variables, funciones, métodos, clases, etc. es importante al escribir código legible. No solo hace que su código sea más legible, le ahorrará tiempo en el futuro.

Objective-C es bastante detallado, pero es muy fácil de leer. Apple utiliza una convención de nombres 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. Añadir significado a los nombres

Como vimos en el consejo anterior, es importante elegir los nombres con inteligencia. Sin embargo, es igualmente importante agregar un significado a los nombres que usa para las variables, funciones, métodos, etc. Esto no solo ayuda a evitar confusiones, sino que hace que el código que escriba 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 añadir, por ejemplo, no siempre es ideal como se puede ver en el siguiente ejemplo.

 bool addUser (usuario u) …

No está claro que agregar usuario se supone que debe hacer ¿Agrega un usuario a una lista de usuarios, a una base de datos oa una lista de personas invitadas a una fiesta? Compara esto con registroUsuario o firmaUsuario. Esto tiene más sentido. ¿Derecha? Eche un vistazo a la siguiente lista para tener una mejor idea de lo que conducimos en.

Palabra Sinónimos
hacer hacer, ejecutar, ejecutar, componer, agregar comienzo lanzar, crear, comenzar, abrir explotar detonar, explotar, arrancar, explotar

5. Nombre Tamaño

A muchos programadores no les gustan los nombres largos, porque son difíciles de recordar y engorrosos de escribir. Por supuesto, un nombre no debería ser ridículamente largo como nuevoClassForNavigationControllerNamedFirstViewController. Esta es Es difícil de recordar y simplemente hace que su código sea feo e ilegible..

Como vimos anteriormente, lo opuesto, los nombres cortos, 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, longitud, o nombre_usuario_longitud? 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 moderno (Entorno de desarrollo integrado). La finalización del código lo ayuda a evitar errores tipográficos y también hace sugerencias para que recordar nombres sea menos doloroso.

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. Echa un vistazo al siguiente ejemplo..

 NSString * link = [[NSString alloc] initWithFormat: @ "http: // localhost: 8080 / WrittingGoodCode / resources / GoodCode / getGoodCode /% @", idCode]; NSURL * infoCode = [NSURL URLWithString: link];

6. Nombrando Booleanos

Los booleanos pueden ser difíciles de nombrar, ya que pueden tener un significado diferente según la forma en que lea o interprete el nombre. En el siguiente fragmento de código, read_password puede significar que la contraseña ha sido leída por el programa, pero también puede significar que el programa debe leer la contraseña.

 BOOL readPassword = YES;

Para evitar este problema, puede cambiar el nombre del booleano anterior a didReadPassword para indicar que la contraseña ha sido leída o shouldReadPassword para mostrar que el programa necesita leer la contraseña. Esto es algo que se ve 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 utilizarse para ayudar a alguien a entender su código. Sin embargo, leer los comentarios también lleva tiempo y si un comentario no agrega mucho valor, entonces ese tiempo se pierde. El siguiente fragmento de código muestra cómo no usar comentarios.

 // Esto sucede cuando se recibe una advertencia de memoria - (void) didReceiveMemoryWarning [super didReceiveMemoryWarning]; // Disponer de cualquier recurso que pueda ser recreado.  // Esto valida los campos - (BOOL) validateFields 

¿Te 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 uso mucho mejor de los comentarios??

 // Determine 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 una base de código de manera rápida y eficiente. Le evita 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 (o más) guía de estilo e incluso la mayoría de las empresas tienen una. ¿Coloca las llaves de un método Objective-C en una línea separada o no??

 - (void) calcula Offset 
 - (void) calcula Offset 

La respuesta es que no importa. No hay una respuesta correcta. Por supuesto, hay guías de estilo que puedes adoptar. Lo importante es que su código es consistente en términos de estilo. Si bien 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 quienquiera que lea su código. Para la mayoría de los desarrolladores, el código feo es el peor tipo de código.

9. Métodos y funciones enfocados

Un error común entre los desarrolladores es tratar de incluir tanta funcionalidad en funciones y métodos. Esto funciona, pero no es elegante y hace que la depuración sea un dolor en el cuello. Su vida, y la de sus colegas, será mucho más fácil si divide los problemas más grandes en pequeños fragmentos y los 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 BOOL result = NO; NSString * documents = nil; NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); if (paths.count) documents = [rutas objectAtIndex: 0]; NSString * basePath = [documents 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]; result = [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atomically: YES];  else NSLog (@ "No se puede crear el directorio debido al error% @ con la información del usuario% @.", error, error.userInfo);  devolver el resultado; 

Si una unidad de código intenta hacer demasiado, a menudo terminas con sentencias condicionales profundamente anidadas, una gran cantidad de comprobación de errores y sentencias condicionales demasiado complejas. Este método hace tres cosas, buscar la ruta del directorio de documentos de la aplicación, buscar y crear la ruta del directorio de archivos y escribir la imagen en el disco. Cada tarea se puede poner en su propio método como se muestra a continuación.

 - (BOOL) saveToImage: (UIImage *) image withFileName: (NSString *) fileName NSString * archivesDirectory = [self applicationArchivesDirectory]; if (! archivesDirectory) devolver NO; // Crear ruta NSString * filePath = [archivesDirectory stringByAppendingPathComponent: fileName]; // Write Image to Disk return [UIImageJPEGRepresentation (image, 8.0) writeToFile: filePath atómicamente: YES]; 
 - (NSString *) applicationDocumentsDirectory NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES); volver paths.count? [rutas objectAtIndex: 0]: nil; 
 - (NSString *) applicationArchivesDirectory NSString * documentsDirectory = [self applicationDocumentsDirectory]; NSString * archivesDirectory = [documentsDirectory stringByAppendingPathComponent: @ "Archives"]; NSFileManager * fm = [Administrador predeterminado de NSFileManager]; 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); devuelve nil  devolver archivesDirectory; 

Esto es mucho más fácil de depurar y mantener. Incluso puedes reutilizar el aplicacióndocumentosdirectorio Método 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 analizado más detenidamente la escritura de código legible mediante la elección inteligente de nombres para variables, funciones y métodos, siendo coherente al escribir código y dividiendo problemas complejos en partes manejables. Si tiene alguna pregunta o comentario, no dude en dejar un comentario a continuación..