Principios SOLID en Java: de la teoría a la práctica
\n\nSOLID 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 crear código flexible, mantenible y escalable. En este artículo, analizaremos cada principio con ejemplos en Java, mostraremos cómo se violan y cómo aplicarlos correctamente.
\n\n¿Por qué son necesarios los principios SOLID?
\nSin SOLID, el código se convierte rápidamente en "espagueti". ¿Te has encontrado con una situación en la que cambiar una clase rompe otras tres? ¿O cuando un método hace demasiado? SOLID resuelve estos problemas. Cada principio es una regla que hace que el código sea:
\n- \n
- Resistente a cambios — los nuevos requisitos no rompen la lógica antigua. \n
- Comprensible — cada clase se encarga de una sola cosa. \n
- Comprobable — es fácil escribir pruebas unitarias. \n
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
\nEnunciado: "Una clase debe tener una sola razón para cambiar".
\nEn palabras simples: una clase debe hacer solo una cosa. Si una clase tiene más de una responsabilidad, es difícil de mantener.
\n\nViolación de SRP (código malo)
\npublic class OrderService {\n public void createOrder(Order order) {\n // Lógica de creación de pedido\n saveToDatabase(order);\n sendEmailConfirmation(order);\n }\n\n private void saveToDatabase(Order order) {\n // Guardar en BD\n }\n\n private void sendEmailConfirmation(Order order) {\n // Enviar email\n }\n}\nProblema: la clase se encarga de la lógica de negocio, del trabajo con la BD y del envío de notificaciones. Un cambio en el servicio de email requerirá modificar OrderService.
Implementación correcta de SRP
\npublic class OrderService {\n private final OrderRepository repository;\n private final EmailService emailService;\n\n public OrderService(OrderRepository repo, EmailService email) {\n this.repository = repo;\n this.emailService = email;\n }\n\n public void createOrder(Order order) {\n repository.save(order);\n emailService.sendConfirmation(order);\n }\n}\n\npublic class OrderRepository {\n public void save(Order order) { /* lógica BD */ }\n}\n\npublic class EmailService {\n public void sendConfirmation(Order order) { /* lógica email */ }\n}\nAhora cada clase tiene una sola razón para cambiar. OrderService solo cambia cuando se modifica el proceso de negocio de creación de pedidos.
2. Open/Closed Principle (OCP) — Principio de abierto/cerrado
\nEnunciado: "Las clases deben estar abiertas para la extensión, pero cerradas para la modificación".
\nEsto significa que debes agregar nueva funcionalidad mediante código nuevo, no modificando el existente.
\n\nViolación de OCP (código malo)
\npublic class AreaCalculator {\n public double calculateArea(Object shape) {\n if (shape instanceof Rectangle) {\n Rectangle r = (Rectangle) shape;\n return r.width * r.height;\n } else if (shape instanceof Circle) {\n Circle c = (Circle) shape;\n return Math.PI * c.radius * c.radius;\n }\n throw new IllegalArgumentException("Unknown shape");\n }\n}\nPara agregar un triángulo, es necesario modificar el método calculateArea — esto viola OCP.
Implementación correcta de OCP
\npublic interface Shape {\n double calculateArea();\n}\n\npublic class Rectangle implements Shape {\n private double width, height;\n public Rectangle(double w, double h) { this.width = w; this.height = h; }\n @Override\n public double calculateArea() { return width * height; }\n}\n\npublic class Circle implements Shape {\n private double radius;\n