SOLID: El Principio Abierto/Cerrado (OCP)

El Principio Abierto/Cerrado, o Open/Closed Principle (OCP), es probablemente uno de los principios más citados en arquitectura de software y, al mismo tiempo, uno de los peor entendidos en la práctica.

· 2 min de lectura

El Principio Abierto/Cerrado fue formulado originalmente por Bertrand Meyer en 1988, mucho antes de que el acrónimo SOLID se hiciera famoso.

La definición clásica suele intimidar: "Un artefacto de software debe estar abierto para la extensión, pero cerrado para la modificación". La primera vez que lees esto, es normal preguntarte: ¿Cómo se supone que voy a cambiar el comportamiento sin modificar el código?.

Lo que NO significa el OCP

Antes de aplicarlo, debemos limpiar los malentendidos comunes. El OCP no significa:

  • Que nunca puedas tocar el código existente.
  • Que todo deba resolverse con herencia.
  • Que tengas que ser un adivino y anticipar todos los cambios futuros.

El OCP no es magia, es diseño. Se trata de crear una arquitectura donde, cuando el negocio pida una nueva funcionalidad, la respuesta sea escribir código nuevo, no modificar la lógica que ya funciona.

Un ejemplo que viola el OCP

Imaginemos que estamos desarrollando el sistema de pagos para una tienda online. Empezamos aceptando pagos con tarjeta de crédito y PayPal. Lo más "rápido" suele ser escribir algo así:

public class ProcesadorDePagos {
    public void procesarPago(String metodo, double monto) {
        if (metodo.equals("TARJETA")) {
            System.out.println("Procesando $" + monto + " con Tarjeta.");
            // Lógica de pasarela de pago
        } else if (metodo.equals("PAYPAL")) {
            System.out.println("Procesando $" + monto + " con PayPal.");
            // Lógica de API de PayPal
        } else {
            throw new IllegalArgumentException("Método no soportado");
        }
    }
}

¿Por qué este código es peligroso?

Aunque funciona, cada vez que el negocio quiera agregar un nuevo método (como Yape, Plin o Transferencia), estarás obligado a abrir y modificar esta clase. Esto aumenta el riesgo de introducir errores en los métodos que ya funcionaban y genera un mantenimiento costoso.

La Solución

Para lograr un diseño escalable, necesitamos separar las cosas que cambian por razones diferentes.

Creamos una abstracción (interfaz) que defina el contrato. El núcleo de nuestra aplicación solo conocerá esta interfaz, sin importarle las implementaciones específicas.

public interface MetodoDePago {
    void procesar(double monto);
}

Ahora, cada nuevo método de pago vive en su propia clase. Si mañana queremos agregar otro, simplemente creamos una clase nueva.

public class PagoConTarjeta implements MetodoDePago {
    public void procesar(double monto) {
        System.out.println("Cobrando $" + monto + " con Tarjeta.");
    }
}

public class PagoConPayPal implements MetodoDePago {
    public void procesar(double monto) {
        System.out.println("Cobrando $" + monto + " con PayPal.");
    }
}

El Patrón Strategy: Orquestando el flujo

Para evitar que el cliente tenga que lidiar con múltiples clases, usamos el patrón Strategy (o una forma de inyección de dependencias). El componente de alto nivel está protegido de los cambios en los detalles de bajo nivel.

public class Checkout {
    private final MetodoDePago metodo;

    public Checkout(MetodoDePago metodo) {
        this.metodo = metodo;
    }

    public void ejecutarCobro(double monto) {
        metodo.procesar(monto);
    }
}

Uso:

MetodoDePago metodo = new PagoConTarjeta();
Checkout checkout = new Checkout(metodo);
checkout.ejecutarCobro(100);

Aplicar el OCP a nivel de arquitectura significa separar las funcionalidades según por qué y cuándo cambian. Al organizar tu código en una jerarquía donde las reglas de negocio están a salvo de los detalles de bajo nivel, construyes un sistema que realmente resiste el paso del tiempo.

Asegúrate de que tu próxima funcionalidad sea una clase nueva, no un parche en una clase vieja.

Actividad Práctica:

Imagina que trabajas en una app para una municipalidad que envía alertas de seguridad a los vecinos. Al principio, la app solo enviaba SMS. Ahora, el alcalde quiere que también envíe notificaciones por WhatsApp y, en el futuro, por Telegram.

Si sigues modificando tu clase principal GestorNotificaciones cada vez que el alcalde pide una red social nueva, estás rompiendo el Principio Abierto/Cerrado (OCP).

Para que el sistema sea cerrado a la modificación pero abierto a la extensión, propón los nombres de las clases e interfaces que crearías:

  1. La Interfaz (El Contrato): ____________________
  2. Las 3 Extensiones (Clases Nuevas):
    • ____________________ (para SMS)
    • ____________________ (para WhatsApp)
    • ____________________ (para Telegram)
  3. La Clase Protegida (El Orquestador): ____________________

Referencias

  • Clean Architecture - Robert C. Martin
  • Design Patterns* - Erich Gamma et al.
  • Agile Software Development: Principles, Patterns, and Practices* - Robert C. Martin