Saltar al contenido

Chain of Responsibility

Un patrón de comportamiento: una solicitud recorre una cadena de manejadores hasta que uno se hace cargo.

6 min. de lectura

Chain of Responsibility es un patrón de comportamiento que permite pasar una solicitud a lo largo de una cadena de manejadores, hasta que uno de ellos la resuelve.

El problema

Antes de confirmar un pedido, AndesShop necesita pasarlo por varias validaciones: que haya stock suficiente, que el método de pago sea válido, que la dirección de envío exista, y que el pedido no dispare las alertas del sistema antifraude. Resolver esto con un único método validate() lleno de if anidados funciona al principio, pero cada validación nueva agranda ese método, y cambiar el orden de las validaciones —o saltear alguna según el tipo de cliente— obliga a reescribir la lógica entera.

La solución

Chain of Responsibility convierte cada validación en un manejador independiente, con una referencia al siguiente manejador de la cadena. Cada uno decide si la resuelve, si la rechaza o si la deja pasar al siguiente. El código que dispara la validación solo conoce el primer eslabón de la cadena — nunca los demás, ni cuántos son.

Estructura de Chain of Responsibility: cada handler conoce solo al siguiente, y el cliente solo conoce al primero.

Ejemplo en Java

abstract class OrderValidator {
    private OrderValidator next;

    public OrderValidator setNext(OrderValidator next) {
        this.next = next;
        return next; // permite encadenar: a.setNext(b).setNext(c)
    }

    public void validate(Order order) {
        if (next != null) {
            next.validate(order);
        }
    }
}

class StockValidator extends OrderValidator {
    @Override
    public void validate(Order order) {
        if (!order.hasAvailableStock()) {
            throw new OrderRejectedException("Insufficient stock");
        }
        super.validate(order); // pasa al siguiente eslabón
    }
}

class PaymentValidator extends OrderValidator {
    @Override
    public void validate(Order order) {
        if (!order.getPaymentMethod().isValid()) {
            throw new OrderRejectedException("Invalid payment method");
        }
        super.validate(order);
    }
}

class FraudValidator extends OrderValidator {
    @Override
    public void validate(Order order) {
        if (order.matchesFraudPattern()) {
            throw new OrderRejectedException("Flagged by fraud rules");
        }
        super.validate(order);
    }
}
// Client code: arma la cadena una vez, y la usa sin conocer sus eslabones internos
OrderValidator chain = new StockValidator();
chain.setNext(new PaymentValidator()).setNext(new FraudValidator());

chain.validate(order);

Cuándo usarlo

  • Cuando más de un objeto puede manejar una solicitud, y el manejador concreto no se conoce de antemano — se decide en tiempo de ejecución.
  • Cuando querés poder cambiar el orden de los manejadores, o agregar/quitar uno, sin tocar el código que dispara la cadena.

Cuándo evitarlo

Un único manejador que siempre termina resolviendo la solicitud no necesita cadena: es indirección sin beneficio, un llamado directo alcanza.

Ventajas y desventajas

VentajasDesventajas
Desacopla quién envía una solicitud de quién la resuelveNo hay garantía de que algún manejador de la cadena efectivamente la resuelva
Permite agregar o reordenar manejadores sin tocar el código clientePuede ser difícil de depurar: seguir el camino real de una solicitud implica rastrear toda la cadena
Sigue el principio de responsabilidad única: cada manejador valida una sola cosa

Relación con otros patrones

  • Suele combinarse con Command: cada solicitud que viaja por la cadena puede estar encapsulada como un Command.
  • Comparte estructura con Decorator (ambos encadenan objetos), pero con intención distinta: Decorator siempre pasa por todos los envoltorios; Chain of Responsibility puede cortar la cadena en cualquier eslabón.

Te sirvió, compartilo

// ¿te sirvió?
// compartilo