Codigo limpio y refactorización
En código limpio, la decisión entre crear métodos genéricos o específicos depende del contexto y de principios de diseño como DRY (Don't Repeat Yourself), SRP (Single Responsibility Principle) y YAGNI (You Ain’t Gonna Need It). Analicemos ambas opciones:
✅ Cuándo crear métodos genéricos
Los métodos genéricos son útiles cuando:
- Evitan la repetición de código. Si varias partes del código requieren una lógica similar, un método genérico evita duplicaciones.
- Son reutilizables. Si la misma operación se aplica en múltiples contextos con variaciones mínimas, es mejor un método genérico con parámetros configurables.
- Facilitan el mantenimiento. Modificar una única función genérica es más fácil que actualizar múltiples métodos con lógica similar.
- Reducen el acoplamiento al hacer que el código sea más modular y reutilizable.
🛑 Cuándo evitar métodos genéricos
- Si la abstracción no aporta claridad. Un método demasiado genérico puede ser difícil de entender y usar.
- Si la lógica es específica para un caso concreto. No tiene sentido generalizar algo que solo se usará en un contexto específico.
- Si es difícil de extender. A veces, agregar más lógica a un método genérico lo hace más complicado que simplemente tener métodos específicos.
✅ Cuándo crear métodos específicos
Los métodos específicos son ideales cuando:
- Tienen una única responsabilidad clara y están diseñados para un caso de uso concreto.
- Aumentan la legibilidad. Un método bien nombrado y con una funcionalidad clara mejora la comprensión del código.
- No se repiten innecesariamente. Si solo se usa en un contexto, no es necesario hacer una abstracción genérica.
- Evitan sobreingeniería. Generalizar demasiado puede llevar a métodos difíciles de entender y mantener.
🛑 Cuándo evitar métodos específicos
- Si el código se repite en varios lugares. Esto va en contra del principio DRY.
- Si hay una clara oportunidad de reutilización en otros módulos o clases.
🔥 Ejemplo práctico 1
Ejemplo Malo: Código muy específico
1public class MostrarNumeros { 2 public static void mostrarNumerosDel1Al100() { 3 for (int i = 1; i <= 100; i++) { 4 System.out.print(i + " "); 5 } 6 System.out.println(); // Salto de línea al final 7 } 8 9 public static void main(String[] args) { 10 mostrarNumerosDel1Al100(); 11 } 12}
Opción mejor: Método más genérico y reutilizable
- Código más flexible gracias a los parámetros inicio y fin.
- Evitamos código rígido que solo mostraba del 1 al 100.
- Ahora podemos mostrar cualquier rango de números fácilmente. 🚀
1public class MostrarNumeros { 2 public static void mostrarNumerosEnRango(int inicio, int fin) { 3 for (int i = inicio; i <= fin; i++) { 4 System.out.print(i + " "); 5 } 6 System.out.println(); // Salto de línea al final 7 } 8 9 public static void main(String[] args) { 10 mostrarNumerosEnRango(5, 20); // Ejemplo: Muestra números del 5 al 20 11 } 12}
🔥 Ejemplo práctico 2
Ejemplo Malo: Código con Efectos Secundarios
Supongamos que tenemos un sistema que filtra usuarios por diferentes criterios.
Aquí estamos repitiendo código con ligeras variaciones.
1import java.util.List; 2import java.util.stream.Collectors; 3 4class Usuario { 5 String nombre; 6 int edad; 7 8 public Usuario(String nombre, int edad) { 9 this.nombre = nombre; 10 this.edad = edad; 11 } 12} 13 14public class FiltradoUsuarios { 15 public static List<Usuario> filtrarPorEdad(List<Usuario> usuarios, int edad) { 16 return usuarios.stream().filter(u -> u.edad == edad).collect(Collectors.toList()); 17 } 18 19 public static List<Usuario> filtrarPorNombre(List<Usuario> usuarios, String nombre) { 20 return usuarios.stream().filter(u -> u.nombre.equals(nombre)).collect(Collectors.toList()); 21 } 22}
Opción mejor: Método genérico con un criterio dinámico
📌 Ventajas de este enfoque:
- Elimina duplicación de código.
- Es más flexible y extensible.
- Usa una función de callback para personalizar el filtrado.
1import java.util.List; 2import java.util.function.Predicate; 3import java.util.stream.Collectors; 4 5public class FiltradoUsuarios { 6 public static List<Usuario> filtrarUsuarios(List<Usuario> usuarios, Predicate<Usuario> criterio) { 7 return usuarios.stream().filter(criterio).collect(Collectors.toList()); 8 } 9 10 public static void main(String[] args) { 11 List<Usuario> usuarios = List.of( 12 new Usuario("Juan", 30), 13 new Usuario("Ana", 25), 14 new Usuario("Carlos", 40) 15 ); 16 17 List<Usuario> mayoresDe30 = filtrarUsuarios(usuarios, u -> u.edad > 30); 18 List<Usuario> conNombreJuan = filtrarUsuarios(usuarios, u -> u.nombre.equals("Juan")); 19 20 System.out.println("Mayores de 30: " + mayoresDe30.size()); 21 System.out.println("Usuarios llamados Juan: " + conNombreJuan.size()); 22 } 23}
📌 Conclusión
No hay una única respuesta correcta, pero en general:
- Si la funcionalidad se repite en varios lugares, conviene un método genérico.
- Si la funcionalidad es muy específica y no se reutilizará, un método específico es mejor.
- Un método genérico no debe ser tan abstracto que dificulte la comprensión del código.
- Aplica el principio KISS (Keep It Simple, Stupid): si un método genérico complica más de lo que ayuda, es mejor evitarlo.
📢 Regla general: Usa métodos genéricos cuando tenga sentido y métodos específicos cuando la claridad sea más importante que la reutilización. 🚀