En esta serie de post vamos a ver algunas técnicas para mejorar la escritura de nuestro código en C#. Cómo hacer que sea limpio, mantenible, disminuir la compresión para las personas del equipo y la técnica de leer código como si fuera una historia.
Por otro lado, Veremos varios ejemplos de código mal escrito y código pobre que comúnmente lo denominamos Code Smells (que huele mal). En los ejemplos detectaremos que está mal en el código y luego veremos cómo corregirlo. Corregir código lo llamaremos Refactoring. Cambiaremos el código, pero no afectamos el comportamiento de quienes lo utilizan.
Una de las herramientas que pueden ayudarnos, es estas tareas, es el uso de ReSharper. Esta gran herramienta nos ayudará a reducir mucho tiempo, pero no es necesaria. Aun así, es posible descargar una versión de prueba de 30 días o bien, si son estudiantes, pueden conseguir una licencia por 1 año tanto de ReSharper como de muchas otras herramientas de JetBrains.
¡¡¡¡Empecemos!!!!
Nombres no significativos
El primer tema que tocaremos es “Nombres no significativos o Poor Names”, en español, nombres pobres. Este es uno de los Code Smells más frecuente que encontraremos en el código. Podemos categorizarlos en varios tipos, veamos:
Nombres misteriosos
SqlCommand sc1;
int od;
void Button1_Click();
class Page1;
Varias cosas que nos preguntaremos en estas líneas de código son: ¿Qué quiere decir sc1, ¿qué es od? ¿Qué es button1 y que tiene de diferente de button2? ¿O a que hace referencia Page? Si no es posible entender con un simple golpe de vista que es, esto nos indicará que estamos en presencia de Code Smell.
Nuestro código debe ser limpio y describir la intención de que es o que va a hacer rápidamente al leerlo. No deberíamos necesitar leer otros fragmentos de código para saber qué están haciendo o qué significan. En el caso de button1, deberíamos leer la implementación para poder determinar que realmente hace o que significa este método. Imaginemos que tenemos más botones: 2, 3, 4. Para determinar en que se distinguen cada uno de ellos, deberíamos ir uno a uno verificando su implementación.
Por esta razón es que se lo denomina misterioso. Tiene secretos por decirlo de alguna forma.
Como seria la forma de correcta de llamarlos, ejemplo:
SqlCommand sr1; | command |
int od; | overdueDays |
void Button1_Click(); | Si el método es para validar si existeCheckExist_Click(); |
class Page1; | Si por ejemplo la página es para customersViewCustomerPage. |
Nombres sin sentido
Meaningless Names o Nombres sin sentido, en español, es otra categoría de nombres pobres. Miremos el siguiente nombre:
public void void BeginValidateFunctionality_SaveClientSideListIDArray(){}
Realmente no es fácil de comprender que hace, debemos ver la implementación. La gran mayoría de las veces, cuando vemos este nombre, significa que el método tiene una gran cantidad de líneas de código y en la mayoría de los casos, esta es la causa que le pongamos un nombre de este tipo.
Tener mucha cantidad de líneas de código nos da el indicio que el método hace muchas cosas. La recomendación es que un método no debería tener más de 10 líneas de código asegurándonos que el método está haciendo solo una cosa. Lo veremos más adelante.
Nombres codificados
Nombres con una codificación o una notación estándar. En el siguiente código:
int iMaxNumberOfCustomers;
Podemos ver que estamos usando la notación húngara. Esta era una notación muy popular para los programas de C++ en los años 80 y 90. En esos momentos era muy difícil saber de qué tipo era una variable, por esto, se usaba como prefijo la primera letra indicándonos que era de un tipo, en este caso i para int. Con el tiempo, muchos programadores la adoptaron en otros lenguajes.
En estos días, no es necesario implementar la notación húngara. Hoy tenemos IDEs muy potentes, como Visual Studio o Eclipse para Java, que con solo posicionar el mouse sobre la variable nos indicará de qué tipo es. Hoy no es necesario ensuciar nuestro código con este tipo de notación.
El ejemplo nos quedaría de la siguiente forma:
int maxCustomers;
Veamos otro ejemplo:
var m_objCollection = new StringCollection();
¿Qué nos dice esto? la verdad no lo sabemos ¿Que almacena esta colección? vemos que dice obj para determinar que son objetos, pero en realidad, no es una colección de objetos sino una colección de string. Otro punto es, ¿Que tiene adentro esta colección de Strings?. Por ejemplo, si tenemos que almacenar una lista de animales, un mejor nombre sería:
var animalNames = new StringCollectioin();
Es un nombre más corto y más significativo relevante la intención del objeto.
Nombres ambiguos o dudosos
Nombres ambiguos o dudosos, nos dan una idea de que puede ser, pero no podemos distinguir correctamente que es. Por ejemplo:
bool MultiSelect(){}
Nos dice que es una selección múltiple de algún tipo de items, pero, debo mirar la implementación del método para saber exactamente lo que hace.
Otro CodeSmell. Veamos otro ejemplo:
int? customerNameId;
¿Qué quiere decir? ¿Si es un incidente porque él puede ser nullable? una de las excepciones más comunes que un ID no pueda serlo. Esto no debería pasar, ¿Porque no es simplemente un int?.
Nombres Ruidosos
Nombres ruidosos. Este es otro nivel de nombres pobres. Veamos los siguientes identificadores:
Customer theCustomer;
List<Customer> listOfApprovedCustomers;
Simplemente podemos llamarlos:
Customers
approvedCustomers
Estos nombres son cortos y realmente indican que son y su objetivo. Ahora veamos algunos ejemplos de que son buenos nombres y cuales son malos nombres:
using System.Drawing;
namespace Poor.Names
{
public class Example
{
public BitMap Method1(string n){
var b = new BitMap(string n);
var g = Graphics.FromImage(b);
g.DrawString("a", SystemFonts.DefaultFont, SystemBrushes.Desktop, new PointF(0,0));
g.DrawString("b", SystemFonts.DefaultFont, SystemBrushes.Desktop, new PointF(0,20));
g.DrawString("c", SystemFonts.DefaultFont, SystemBrushes.Desktop, new PointF(0,40));
return b;
}
}
}
En este fragmento de código, podemos ver que el nombre del método no nos dice nada, lo cambiaremos por GreateImage.
Cuidado, recordemos que al cambiar el nombre todas las referencias a este objeto se romperán, dejarán de funcionar y de compilar. Muchos usan Find and Replace para arreglar, pero no es un método fiable. Muchas veces tendremos diferentes identificadores con el mismo nombre en diferentes contextos. Aquí, es donde podemos usar una herramienta como Resharper y su funcionalidad “Rename Refactoring”. Resharper buscará todos los referenciados a este método y los reemplazará sin romper nuestro código.
Una convención estándar
Cada equipo de programadores tiene su propio grupo de convención de nombres. No está mal, pero es bueno tener una general. Nos ayudaría a incorporar rápidamente nuevas personas al equipo. Veamos algunos ejemplos:
Product GetProduct(int _id);
Product Save(Product Customer);
private int productId;
Veamos, el primer método tiene las primeras letras de cada palabra en mayúsculas, en el segundo método solamente la segunda palabra tiene mayúscula y por último la declaración de la variable está del mismo modo. En el caso de los parámetros vemos que en el primer método tiene un _ pero en el segundo método no lo tiene.
.Net Framework existen básicamente 2 tipos de convenciones. Pascal Case y Camel Case:
- En PascalCase, la primera letra de cada palabra se escribe con mayúsculas
- En CamelCase, la primera letra de la primera palabra se escribe con minúsculas y las siguiente primeras letras de cada palabra se escriben con mayúsculas.
public class Product
{
private int _id;
public string Name { get; set;}
public void SetStock(int amount)
{
var stock = amount;
}
}
Es este ejemplo de código podemos ver que el nombre de la clase, la propiedad y el método se encuentra denotado con Pascal Case y lo que se encuentra en violeta como Camel Case. Entonces:
- Pascal Case
- Nombre de clase.
- Métodos.
- Propiedades.
- Camel case
- variables (excepto en métodos privados que se usa un _ delante de ellos)
- parámetros de métodos
- variables locales.
Cuando escribimos nuestro código es bueno seguir mínimamente estas convenciones y haremos que todo el equipo la siga. Al hacerlo, nos dará una buena base de código limpio y se verá como que solo una persona lo ha escrito. Si vemos que en algún lado nuestro código no está escrito de esta manera es una buena idea repararlo (Refactorizar).
Conclusión
Hemos visto algunas buenas prácticas para nombrar nuestros objetos y clases. También hemos visto una estándar simple para implementar en nuestro equipo. Para Resumir:
- Ni muy cortos, ni muy largos.
- Deben significar algo.
- Revelar una intención.
- Que tenga relación con el contexto del problema.
- Seguir mínimamente la convención de nombres recomendada por el fabricante.
En próximos posts veremos más temas para tener un código sano, limpio, extensible y mantenible.