← dev-notes
SOLID · 02 / 05

OCP
OPEN / CLOSED

Extender comportamiento sin modificar código ya probado en producción

Extensión Invariantes estables Polimorfismo
01 Definición formal
FORMAL · DEFINICIÓN

Los artefactos de software deben estar abiertos para su extensión y cerrados para su modificación: puedes añadir nuevos comportamientos ampliando el sistema (p. ej. nuevas implementaciones o plugins) sin alterar el código fuente de los módulos existentes que ya funcionan.

Bertrand Meyer acuñó el principio; en OO suele materializarse con abstracciones estables (interfaces o clases base) y nuevas clases concretas que las implementan.

02 Idea clave

Cada vez que añades un if o un case central que crece con cada regla de negocio nuevo, modificas un módulo compartido y aumentas el riesgo de regresiones. El OCP empuja a modelar variaciones como tipos intercambiables detrás de una misma operación (estrategia, cadena de responsabilidad, etc.).

03 Cuando se rompe · Java

Cálculo de subtotales en una cesta: cada nuevo tipo de línea (envío, suscripción, bundle) obliga a editar el mismo método.

ROMPE OCP — SWITCH QUE CRECE
public final class CartSubtotalCalculator {

    public Money subtotal(CartLine line) {
        return switch (line.type()) {
            case PRODUCT  -> line.unitPrice().multiply(line.quantity());
            case SHIPPING_FEE -> line.flatFee();
            case SUBSCRIPTION -> line.periodPrice(); // añadido después
            // mañana: GIFT_WRAP -> hay que tocar OTRA VEZ esta clase
            default -> throw new IllegalStateException("tipo desconocido");
        };
    }
}
04 Aplicación adecuada · Java

Cada tipo de línea aporta su propia estrategia; el calculador solo delega en el contrato CartLine.

RESPETA OCP — EXTENDER CON NUEVAS CLASES
public interface CartLine {
    Money addTo(Money accumulator);
}

public final class ProductLine implements CartLine {
    private final Money unitPrice;
    private final int qty;
    // ...
    @Override public Money addTo(Money acc) {
        return acc.add(unitPrice.multiply(qty));
    }
}

public final class ShippingFeeLine implements CartLine {
    private final Money flatFee;
    @Override public Money addTo(Money acc) { return acc.add(flatFee); }
}

public final class CartSubtotalCalculator {
    public Money subtotal(List<CartLine> lines) {
        return lines.stream()
            .reduce(Money.zero(), (acc, line) -> line.addTo(acc), Money::add);
    }
}

Una línea GiftWrapLine nueva es solo otra clase que implementa CartLine; el calculador cerrado no se recompila por ese motivo.

05 Relación con patrones
PUENTE SOLID ↔ GOF
  • Strategy: cada regla de subtotal se encapsula y se agrega sin tocar el cálculo central.
  • Chain of Responsibility: útil cuando reglas se aplican en secuencia y pueden cortarse.
  • Evita sobreingeniería: un if simple puede bastar cuando no hay variación esperada.