Chain of Responsibility (patrón de diseño)

De Wikipedia, la enciclopedia libre
Saltar a: navegación, búsqueda

El patrón de diseño Chain of Responsibility es un patrón de comportamiento que evita acoplar el emisor de una petición a su receptor dando a más de un objeto la posibilidad de responder a una petición. Para ello, se encadenan los receptores y pasa la petición a través de la cadena hasta que es procesada por algún objeto. Este patrón es utilizado a menudo en el contexto de las interfaces gráficas de usuario donde un objeto puede contener varios objetos. Según si el ambiente de ventanas genera eventos, los objetos los manejan o los pasan.

Motivación[editar]

Supongamos un servicio de ayuda sensible al contexto para una interfaz gráfica. El usuario puede obtener ayuda en cualquier parte de la interfaz pulsando con el ratón sobre ella. La ayuda proporcionada depende de la parte de la interfaz que se haya seleccionado así como de su contexto. Si no existe información de ayuda específica para esa parte el sistema debería mostrar un mensaje de ayuda más general sobre el contexto inmediato. El problema es que el objeto que en última instancia proporciona la ayuda no conoce explícitamente al objeto (por ejemplo, el botón) que inicializa la petición. Necesitamos un modo de desacoplar el botón que da lugar a la petición de ayuda de los objetos que podrían proporcionar dicha información. El patrón Cadena de Responsabilidad define cómo hacerlo. La idea de este patrón es desacoplar a los emisores y a los receptores dándole a varios objetos la posibilidad de tratar una petición. La petición se pasa a través de una cadena de objetos hasta que es procesada por uno de ellos.

Motivacion2ES.png

Supongamos que el usuario solicita ayuda sobre un botón denominado "Imprimir", el cuál se encuentra en una instancia de DialogoDeImpresion. El siguiente diagrama muestra cómo la petición de ayuda se reenvía

Motivacion3ES.jpg

En este caso, la petición no es procesada ni por unBotonDeImpresion ni por unDialogoDeImpresion; se detiene en unaAplicación que podrá procesarla u obviarla. El cliente que dio origen a la petición no tiene ninguna referencia directa al objeto que finalmente la satisface. Para reenviar la petición a lo largo de la cadena, y grantizar que los receptores permanecen implícitos, cada objeto de la cadena comparte una interfaz común para procesar peticiones y para acceder a su sucesor. Por ejemplo, en este sistema de ayuda podría definirse una clase ManejadorDeAyuda.

Motivacion1ES.jpg

Las clases Botón, Dialogo y Aplicación usan las operaciones de ManejadorDeAyuda para tratar peticiones de ayuda. La operación ManejarAyuda de ManejadorDeAyuda reenvía la petición al sucesor de manera predeterminada. Las subclases pueden redefinir esta operación para proporcionar ayuda en determinadas circunstancias; en caso contrario, pueden usar la implementación predeterminada para reenviar la petición.

Aplicabilidad[editar]

El patrón Cadena de Responsabilidad debe usarse cuando:

  • hay más de un objeto que puede manejar una petición, y el manejador no se conoce a priori, sino que debería determinarse automáticamente.
  • se quiere enviar una petición a un objeto entre varios sin especificar explícitamente el receptor.
  • el conjunto de objetos que pueden tratar una petición debería ser especificado dinámicamente.

Estructura[editar]

Estructura general

Participantes[editar]

  • Manejador: define una interfaz para tratar las peticiones. Opcionalmente, implementa el enlace al sucesor.
  • ManejadorConcreto: trata las peticiones de las que es responsable; si el ManejadorConcreto puede manejar la petición, lo hace; en caso contrario la reenvía a su sucesor.
  • Cliente: inicializa la petición a un Manejador Concreto de la cadena.

Colaboraciones[editar]

Cuando un cliente envía una petición, ésta se propaga a través de la cadena hasta que un objeto ManejadorConcreto se hace responsable de procesarla.

Consecuencias[editar]

Las ventajas de este patrón son:

  • Reduce el acoplamiento. El patrón libera a un objeto de tener que saber qué otro objeto maneja una petición. Ni el receptor ni el emisor se conocen explícitamente entre ellos, y un objeto de la cadena tampoco tiene que conocer la estructura de ésta. Por lo tanto, simplifica las interconexiones entre objetos. En vez de que los objetos mantengan referencias a todos los posibles receptores, sólo tienen una única referencia a su sucesor.
  • Añade flexibilidad para asignar responsabilidades a objetos. Se pueden añadir o cambiar responsabilidades entre objetos para tratar una petición modificando la cadena de ejecución en tiempo de ejecución. Esto se puede combinar con la herencia para especializar los manejadores estáticamente.

Por otra parte presenta el inconveniente de no garantizar la recepción. Dado que las peticiones no tienen un receptor explícito, no hay garantías de que sean manejadas. La petición puede alcanzar el final de la cadena sin haber sido procesada.

Implementación[editar]

  • Implementación de la cadena sucesora. Hay dos formas posibles de implementarla:
  1. Definir nuevos enlaces (normalmente en el Manejador, pero también podría ser en los objetos ManejadorConcreto).
  2. Usar enlaces existentes (otras asociaciones existentes). Por ejemplo, en el patrón Composición puede existir ya un enlace al padre que puede utilizarse para definir la cadena de responsabilidad sin necesidad de añadir otra asociación.
  • Conexión de los sucesores. Si no hay referencias preexistentes para definir una cadena, entonces tendremos que introducirlas nosotros mismos. En este caso, el Manejador define la interfaz y además, se encarga de mantener el sucesor. Esto permite que el manejador proporcione una implementación predeterminada de ManejarPetición que reenvíe la petición al sucesor (si hay alguno). Si una subclase de ManejadorConcreto no está interesada en dicha petición, no tiene que redefinir la operación de reenvío.
  • Representación de peticiones. Hay varias opciones para representar las peticiones:
  1. Una petición es una invocación a una operación insertada en el código. Esto resulta conveniente y seguro, pero sólo se pueden reenviar el conjunto prefijado de peticiones que define la clase Manejador.
  2. Una única función manejadora que reciba un código de petición como parámetro. Esto permite un número arbitrario de peticiones pero emisor y receptor deben ponerse de acuerdo sobre cómo codificarse la petición.

Patrones relacionados[editar]

Este patrón se aplica en ocasiones con el patrón Composición. En él, los padres de los componentes pueden actuar como sucesores.

Ejemplo[editar]

Diagrama de clases[editar]

DClasesES.jpg

Java[editar]

public class Cliente {
 
    public static void main(String argv[]) {
      Unidad smith  = new Coronel("Smith", null);
      Unidad truman = new Coronel("Truman", "Tomar posición enemiga");
      Unidad ryan   = new Soldado("Ryan");
      Unidad rambo  = new Soldado("Rambo");
 
      System.out.println(rambo.orden());    // rambo ->
 
      rambo.establecerMando(truman);
      System.out.println(rambo.orden());    // rambo -> truman
 
      ryan.establecerMando(rambo);
      System.out.println(ryan.orden());     // ryan -> rambo -> truman
    }
}
/**
 * La clase Unidad representa la clase abstracta manejadora de la cadena de responsabilidad.
 * El servicio delegado en la cadena es la solicitud de una orden al mando directo
 */
 
public abstract class Unidad {
 
    /* en el constructor, además de un nombre para la unidad, se inicializa la referencia
       que implementa la cadena de responsabilidad (_mando): en principio no hay sucesor */
 
    public Unidad(String nombre) { 
        _mando = null; 
        _nombre = nombre;
    }
 
    public String toString() { return _nombre; }
 
    // cambia el mando de una unidad (modifica cadena de responsabilidad)
 
    public void establecerMando(Unidad mando) { _mando = mando; }
 
    /* comportamiento por defecto de la cadena: delegar en el mando directo o, si se 
       alcanza el final de la cadena, utilizar una resolución por defecto (sin orden) */
 
    public String orden() {
        return (_mando != null ? _mando.orden() : "(sin orden)"); 
    }
 
    private Unidad _mando;
    private String _nombre;
}
/**
 * La clase Coronel modifica ligeramente el comportamiento por defecto de la cadena de
 * responsabilidad: si el coronel tiene una orden específica, utiliza ésta para resolver
 * el servicio. Si no tiene una orden específica (_orden==null), emplea el comportamiento
 * convencional de las unidades
 */
 
public class Coronel extends Unidad {
 
    // inicializa la parte de unidad e inicializa el estado propio del Coronel (_orden)
 
    public Coronel(String nombre, String orden) {
      super(nombre);
      _orden = orden;
    }
 
    /* refinamiento del servicio que utiliza la cadena de responsabilidad, resolviendo
       localmente si tiene órdenes específicas o comportándose convencionalmente en
       caso contrario */
 
    public String orden()    { return (_orden != null ? _orden : super.orden()); }
 
    public String toString() { return ("Coronel " + super.toString()); }
 
    private String _orden;
}
/**
 * Esta clase es una extensión instanciable de la superclase Unidad que respeta el
 * comportamiento por defecto de la cadena de responsabilidad
 */
 
public class Soldado extends Unidad {
 
    // el constructor sólo tiene que inicializar la parte correspondiente a la superclase
 
    public Soldado(String nombre) {
        super(nombre);
    }
 
    public String toString() { return ("Soldado " + super.toString()); }
}

Diagrama de secuencia[editar]

SecuenciaES.jpg

Enlaces Externos[editar]