El Principio Abierto/Cerrado, o Open–Closed Principle (OCP), es el segundo principio de SOLID y, sin exagerar, uno de los más importantes para construir software que sobreviva al paso del tiempo.
Fue formulado originalmente por Bertrand Meyer en 1988, mucho antes de que SOLID existiera como acrónimo. Y su definición clásica es tan elegante como intimidante:
“Un artefacto de software debe estar abierto para extensión, pero cerrado para modificación.”
La primera vez que lees esto, es normal pensar: “¿Cómo se supone que voy a cambiar el comportamiento sin modificar el código?”
Buena pregunta. Vamos paso a paso.
Qué NO significa el Principio Abierto/Cerrado
Antes de explicar qué es el OCP, conviene aclarar qué no es, porque aquí también hay muchos malentendidos.
El OCP no significa:
- Que nunca puedas tocar código existente
- Que todo deba resolverse con herencia
- Que tengas que anticipar todos los cambios futuros
- Que tu sistema nunca vuelva a modificarse
Nada de eso es realista.
El OCP no es magia, es diseño.
Para entenderlo, Clean Architecture nos propone un experimento. Imagina un sistema que muestra un resumen financiero en una página web.
- Los datos son desplazables (scroll).
- Los números negativos aparecen en rojo.
Todo funciona perfecto hasta que los jefes (stakeholders) piden lo siguiente: "Queremos ese mismo reporte, pero para imprimir en blanco y negro".
- Ahora necesita paginación y encabezados.
- Los números negativos no pueden ser rojos (es blanco y negro), deben estar entre paréntesis
( -500 ).
Obviamente, tienes que escribir código nuevo. Pero, ¿cuánto código viejo tienes que cambiar?. En una buena arquitectura, la respuesta debería ser: Cero (o el mínimo posible).
Divide y Protege
Para lograr esto, necesitamos separar las cosas que cambian por razones diferentes (SRP) y organizar sus dependencias (DIP).
En nuestro ejemplo, generar el reporte implica dos responsabilidades distintas:
- Cálculo: Calcular los datos financieros (Lógica de Negocio).
- Presentación: Formatear esos datos para Web o para PDF/Impresión.
Implementación con Código
Vamos a traducir los diagramas del libro a código Java para que veas cómo se aplica el OCP usando Interfaces para invertir las dependencias.
1. El Núcleo (Cerrado a modificación)
Este es nuestro Interactor. Contiene las reglas de negocio (calcular finanzas). No sabe si el reporte va a una web, a una impresora o a un satélite. Es el componente de más "alto nivel" y el más protegido.
// Interfaz que usa el Interactor para enviar datos (No sabe a quién)
public interface FinancialReportPresenter {
void present(FinancialReportResponse response);
}
// El Interactor: Contiene la lógica pura
public class FinancialReportGenerator {
private FinancialReportPresenter presenter;
public FinancialReportGenerator(FinancialReportPresenter presenter) {
this.presenter = presenter;
}
public void generate() {
// 1. Obtiene datos de la BD (a través de otra interfaz)
FinancialEntities data = database.getEntities();
// 2. Aplica reglas de negocio
FinancialReportResponse response = new FinancialReportResponse();
// ... cálculos complejos ...
// 3. Entrega el resultado al presentador
presenter.present(response);
}
}
2. Las Extensiones (Abiertas a extensión)
Aquí es donde ocurre la magia. Creamos implementaciones específicas para cada formato. Si mañana queremos un reporte en Excel, creamos una clase nueva y no tocamos el Interactor.
// Implementación para WEB
public class ScreenPresenter implements FinancialReportPresenter {
public void present(FinancialReportResponse response) {
// Lógica para poner números en ROJO y formato HTML
System.out.println("<span style='color:red'>" + response.amount + "</span>");
}
}
// Implementación para IMPRESIÓN (PDF)
public class PrintPresenter implements FinancialReportPresenter {
public void present(FinancialReportResponse response) {
// Lógica para usar PARÉNTESIS y paginación
System.out.println("(" + response.amount + ")");
}
}
El punto clave que menciona el capítulo es cómo organizamos las flechas de dependencia. Si el componente A debe ser protegido de cambios en el componente B, entonces B debe depender de A.
Imagina una jerarquía de niveles:
- Interactor (Reglas de Negocio): Es el rey. No depende de nadie. Está protegido de todo. Si cambia la base de datos o el HTML, el Interactor ni se entera.
- Controller: Depende del Interactor, pero protege al Interactor de la Web.
- Presenters (Presentadores): Dependen del Controller y del Interactor.
- Views (Vistas/Web/BD): Son el nivel más bajo. Son "periféricos". Nadie depende de ellas, así que pueden romperse y cambiarse sin afectar al núcleo.
En el diagrama de componentes del libro, todas las flechas cruzan los límites en una sola dirección: hacia el componente que queremos proteger.
Aplicar el OCP a nivel de arquitectura significa separar las funcionalidades según por qué y cuándo cambian, y organizarlas en una jerarquía donde los componentes de alto nivel (reglas de negocio) están a salvo de los cambios en los detalles de bajo nivel (UI, Bases de Datos).
Así que la próxima vez que te pidan "solo un cambio de formato", asegúrate de que tu arquitectura te permita hacerlo creando una clase nueva, y no operando a corazón abierto en tu lógica de negocio.