Clean Code en C#: El camino hacia el código profesional
Todo desarrollador sueña con escribir código que no solo funcione, sino que también sea fácil de leer, mantener y ampliar. El concepto de "código limpio" (Clean Code), popularizado por Robert Martin, no es solo un conjunto de reglas, sino una filosofía de profesionalismo. En este artículo, analizaremos los principios clave de Clean Code aplicados al lenguaje C# y la plataforma .NET, utilizando ejemplos simples y claros para principiantes. Aprenderá cómo transformar el código "sucio" en una solución elegante y confiable.
1. Nombres con significado: El primer paso hacia la limpieza
La parte más importante del código limpio es la nomenclatura. El nombre de una variable, método, clase o interfaz debe responder a las preguntas "¿Qué es esto?" o "¿Qué hace?". Los buenos nombres eliminan la necesidad de escribir comentarios.
Ejemplos buenos y malos
Evite las abreviaturas de una sola letra (excepto para contadores de bucles) y los nombres abstractos.
// MALO: Nombres confusosint d; // elapsed time in daysList<string> l = new List<string>();
public void Proc(int x) { // ...}
// BUENO: Nombres con significadoint elapsedTimeInDays;List<string> customerNames = new List<string>();
public void ProcessOrder(int orderId){ // ...}Reglas para C#:
- Clases y métodos —
PascalCase(por ejemplo,OrderService,CalculateTotal). - Variables locales y parámetros —
camelCase(por ejemplo,orderId,customerName). - Interfaces — comienzan con la letra
I(por ejemplo,IRepository). - Evite la "notación húngara" (como
strName). El tipo de variable debe ser obvio por el contexto.
2. Funciones: Pequeñas y que hacen una sola cosa
Una función (o método) debe ser pequeña y realizar solo una tarea (Principio de Responsabilidad Única a nivel de métodos). Si una función hace varias cosas, debe dividirse en funciones más pequeñas.
Refactorización de una función grande
Imagine que necesitamos guardar un pedido y enviar una notificación. El código malo lo hace todo de una vez.
// MALO: La función hace demasiadopublic void SaveOrderAndNotify(Order order){ // 1. Validación if (order.Items.Count == 0) throw new Exception("Empty order");
// 2. Guardar en BD (hipotéticamente) Console.WriteLine($"Order {order.Id} saved.");
// 3. Enviar email Console.WriteLine($"Notification sent to {order.CustomerEmail}.");}
// BUENO: División en funciones pequeñaspublic void ProcessOrder(Order order){ ValidateOrder(order); SaveOrder(order); SendOrderConfirmation(order);}
private void ValidateOrder(Order order){ if (order.Items.Count == 0) throw new InvalidOperationException("Cannot process an empty order.");}
private void SaveOrder(Order order){ // Lógica de guardado Console.WriteLine($"Order {order.Id} saved.");}
private void SendOrderConfirmation(Order order){ // Lógica de envío Console.WriteLine($"Notification sent to {order.CustomerEmail}.");}Consejo: Intente que el nivel de abstracción dentro de una misma función sea uniforme. No mezcle el trabajo con la base de datos y el formato de texto en un mismo método.
3. Manejo de errores y excepciones
El código limpio maneja los errores de manera predecible. En C#, para esto se utilizan excepciones. Evite devolver "números mágicos" (por ejemplo, -1) o null para indicar un error.
// MALO: Devolver null en caso de errorpublic User FindUser(int id){ if (id <= 0) return null; // ¿Qué debe hacer el código que llama? // ... búsqueda return n