Principios SOLID en Java: de la teoría a la práctica
SOLID es el acrónimo de cinco principios fundamentales de la programación orientada a objetos, formulados por Robert Martin (Tío Bob). Estos principios ayudan a los desarrolladores a crear código flexible, mantenible y fácilmente extensible. En este artículo analizaremos cada principio SOLID con ejemplos en Java, desde la teoría simple hasta la práctica real.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Teoría: Una clase debe tener solo una razón para cambiar. En otras palabras, cada clase debe ser responsable de exactamente una funcionalidad.
Violación de SRP:
public class Employee { public void calculatePay() { /* cálculo del salario */ } public void saveToDatabase() { /* guardar en BD */ } public void generateReport() { /* generar informe */ }}Solución:
public class Employee { /* solo datos */ }
public class PayCalculator { public void calculatePay(Employee emp) { /* ... */ }}
public class EmployeeRepository { public void save(Employee emp) { /* ... */ }}
public class ReportGenerator { public void generate(Employee emp) { /* ... */ }}Ahora cada clase tiene una responsabilidad clara, lo que simplifica las pruebas y el mantenimiento.
2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
Teoría: Las entidades de software (clases, módulos, funciones) deben estar abiertas para la extensión, pero cerradas para la modificación.
Violación de OCP:
public class ShapeDrawer { public void draw(String shapeType) { if ("circle".equals(shapeType)) { // dibujar círculo } else if ("rectangle".equals(shapeType)) { // dibujar rectángulo } }}Solución usando polimorfismo:
public interface Shape { void draw();}
public class Circle implements Shape { @Override public void draw() { /* dibujar círculo */ }}
public class Rectangle implements Shape { @Override public void draw() { /* dibujar rectángulo */ }}
public class ShapeDrawer { public void draw(Shape shape) { shape.draw(); }}Ahora, para agregar una nueva figura (por ejemplo, Triangle), no es necesario modificar el código existente; simplemente creamos una nueva clase que implemente la interfaz Shape.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Liskov
Teoría: Los objetos en un programa deben ser reemplazables por instancias de sus subtipos sin alterar la corrección del programa. Si S es un subtipo de T, entonces los objetos de tipo T pueden ser reemplazados por objetos de tipo S sin que el programa falle.
Violación clásica de LSP:
public class Rectangle { protected int width, height; public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } public int getArea() { return width * height; }}
public class Square extends Rectangle { @Override public void setWidth(int w) { super.setWidth(w); super.setHeight(w); } @Override public void setHeight(int h) { super.setWidth(h); super.setHeight(h); }}Un cliente que trabaja con Rectangle espera que cambiar el ancho no afecte la altura. El cuadrado viola esta expectativa. Solución: Usar una interfaz común o clases separadas sin herencia.
4. Interface Segregation Principle (ISP) — Principio de segregación de interfaces
Teoría: Los clientes no deben depender de métodos que no utilizan. Es mejor tener varias interfaces especializadas que una interfaz "gruesa".
Violación de ISP: