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

Na Galipedia, a Wikipedia en galego.

Cadea de Responsabilidade (en inglés "Chain of Responsability") é un patrón de deseño que permite xestionar unha petición a máis de un obxecto mediante o encadeamento de receptores, que se pasan a petición ó longo da cadea ata que un dos obxectos manipula dita petición.

Un patrón de cadea de responsabilidade é a miúdo utilizado no contexto de interfaces gráficas de usuario onde un obxecto pode conter varios outros obxectos. Segundo o ambiente de ventás xera eventos, os obxectos ou os manexan ou los pasan.

Motivación[editar | editar a fonte]

  • Supoñamos un servizo de axuda sensible ó contexto para unha interface gráfica de usuario. O usuario pode obter información de axuda en calquera parte da mesma simplemente pulsando co rato sobre ela. A axuda proporcionada depende da parte da interface que se selecciona así como do seu contexto. Se non existe información de axuda específica para esa parte da interface o sistema de axuda debería mostrar un mensaxe de axuda más xeral sobre o contexto inmediato.
  • Se o obxecto "actual" seleccionado da cadea ten información específica debe presentala e manexar a petición; en caso contrario, debe enviar a petición (mensaxe) ó seu sucesor na cadea de responsabilidade.
  • O problema é que o obxecto que en última instancia proporciona a axuda non coñece explicitamente o obxecto que inicia a petición de axuda. Necesitamos un modo de desacoplar o botón que da lugar á petición de axuda dos obxectos que poderían proporcionar dita información. O patrón Cadea de Responsabilidade define como facer isto: a idea é desacoplar ós emisores e ós receptores dándolle a varios obxectos a posibilidade de tratar unha petición. Esta petición pasará a través da cadea de obxectos ata que sexa procesada por un deles.
Motivacion2GL.png

Se un usuario solicita axuda sobre un botón denominado "Imprimir". O botón encóntrase nunha instancia de DialogoDeImpresion. O seguinte diagrama de interacción mostra como a petición de axuda se reenvía a través da cadea.

Motivacion3GL.jpg

Nese caso a petición non é procesada nin por unBotonDeImpresion nin por unDialogoDeImpresion; detense en unhaAplicacion, quen pode procesala ou ignorala. O cliente que deu orixe á petición non ten ningunha referencia directa ó obxecto que finalmente a satisfai.

Para reenviar a petición ó longo da cadea e para garantir que os receptores permanecen implícitos, cada obxecto da cadea comparte unha interface común para procesar peticións e para acceder ó seu sucesor na cadea. Por exemplo, neste sistema de axuda podería definirse unha clase ManexadorDeAxuda.

Motivacion1GL.jpg

Aplicabilidade[editar | editar a fonte]

  • Cando hai máis de un obxecto que pode manexar unha petición, pero non se sabe a priori cal deles o ten que facer.
  • Se non se desexa identificar explicitamente quen é o receptor da petición.
  • Cando os obxectos que poden manexar a petición varían dinamicamente.

Estrutura[editar | editar a fonte]

EstructuraGL2.jpg

Participantes[editar | editar a fonte]

  • Manexador : A interface que define o método usado para pasar unha mensaxe ó seguinte handler. A mensaxe é normalmente unha chamada a un método. Ademais, frecuentemente define tamén o enlace ó sucesor na cadea.
  • Manexador concreto : Unha clase que implementa a interface Handler e mantén unha referencia ó seguinteHandler. Esta referencia pode ser asignada no construtor da clase ou a través dun método setter. A implementación do método de manexo de mensaxes pode chamar a un método para manexar as peticións das que é responsable ou, en caso contrario, se non pode xestionar unha delas delega a petición no seu sucesor.
  • Cliente : Envía unha petición a algún Manexador Concreto da cadea.

Colaboracións[editar | editar a fonte]

  • Cando un cliente inicia unha petición, esta propágase a través da cadea ata que un Manexador Concreto asume a responsabilidade de xestionala.

Consecuencias[editar | editar a fonte]

  • Reduce o acoplamento de obxectos: tanto o manexador como o cliente non coñecen a estrutura da cadea en si. Ademais, coñécese unicamente como chegar ó primeiro elo da cadea, pero non se sabe a implementacion que hai de por medio.
  • Flexibilidade engadida á hora de engadir responsabilidades a obxectos: por exemplo, no caso de ter un departamento de recepción de documentos que está moi ocupado atendendo a varios clientes á vez, pódese facer que a secretaría reciba algúns dos documentos e que o porteiro faga o mesmo.
  • A Cadea de Responsabilidades ofrece unha gran flexibilidade no procesamento de eventos para unha aplicación, xa que domina o manexo de eventos complexos dividindo as responsabilidades a través dun número de elementos simples, permitindo a un grupo de clases comportarse como un todo.
  • A recepción da petición non está garantida: ó non haber un receptor explícito para as mensaxes, non se garante que esta chegue nin tampouco se garante que cando chegue se procese de maneira adecuada (inconveniente).

Implementación[editar | editar a fonte]

Cadea de sucesores:

  • Definir novos enlaces (normalmente no Manexador, pero tamén podería ser nos obxector ManexadorConcreto).
  • Usar enlaces existentes (outras asociacións existentes). Por exemplo, no patrón Composición pode existir xa un enlace ó pai que pode utilizarse para definir a cadea de responsabilidade sen necesidade de engadir outra asociación.
  • Conexión de sucesores. Se non hai referencias preexistentes para definir unha cadea, entón teremos que introducilas nosoutros mesmos. Neste caso, o Manexador define a interface e ademais encárgase de manter o sucesor. Isto permite que o Manexador proporcione unha implementación predeterminada de ManexarPetición que reenvíe a petición ó sucesor (se hai algún). Se unha subclase de ManexadorConcreto non está interesada en dita petición, non ten que redefinir a operación de reenvío.

Representación das peticións

  • un método por cada petición. Isto resulta adecuado e seguro, pero só se pode reenviar o conxunto prefixado de peticións que define a clase Manexador.
  • único método parametrizado para distinguir distintas peticións. Isto permite un número arbitrario de peticións pero emisor e receptor deben poñerse de acordo sobre como codificar a petición.

Patróns relacionados[editar | editar a fonte]

Este patrón pódese aplicar conxuntamente co patrón Composite. Neste, os pais dos compoñentes poden actuar como sucesores.

Exemplo de implementación[editar | editar a fonte]

DClasesGL.jpg
public class Cliente {
 
    public static void main(String argv[]) {
        Unidade smith  = new Coronel("Smith", null);
        Unidade truman = new Coronel("Truman", "Tomar posición inimiga");
        Unidade ryan   = new Soldado("Ryan");
        Unidade rambo  = new Soldado("Rambo");
 
        System.out.println(rambo.orde());    // rambo ->
 
        rambo.establecerMando(truman);
        System.out.println(rambo.orde());    // rambo -> truman
 
        ryan.establecerMando(rambo);
        System.out.println(ryan.orde());     // ryan -> rambo -> truman
    }
}
/**
 * A clase Unidade representa a clase abstracta manexadora da cadea de responsabilidade.
 * O servizo delegado na cadea é a solicitude dunha orde ó mando directo
 */
 
public abstract class Unidade {
 
    /* No construtor, ademais dun nome para a unidade, iníciase a referencia que implementa a cadea de responsabilidade (_mando): en principio no hai sucesor */
 
    public Unidade(String nome) { 
        _mando = null; 
        _nome = nome;
    }
 
    public String toString() { return _nome; }
 
    // cambia o mando dunha unidade (modifica cadea de responsabilidade)
 
    public void establecerMando(Unidade mando) { _mando = mando; }
 
    /* comportamento por defecto da cadea: delegar no mando directo ou, se se alcanza o final da cadea, utilizar unha resolución por defecto (sen orde) */
 
    public String orde() {
        return (_mando != null ? _mando.orde() : "(sen orde)"); 
    }
 
    private Unidade _mando;
    private String _nome;
}
/**
 * A clase Coronel modifica lixeiramente o comportamento por defecto da cadea de
 * responsabilidade: se o coronel ten unha orde específica, utiliza esta para resolver
 * o servizo. Se non ten unha orde específica (_orde==null), emprega o comportamento
 * convencional das unidades
 */
 
public class Coronel extends Unidade {
 
    // inicia a parte de unidade e inicia o estado propio do Coronel (_orde)
 
    public Coronel(String nome, String orde) {
      super(nome);
      _orde = orde;
    }
 
    /* refinamento do servizo que emprega a cadea de responsabilidade, resolvendo
       localmente se ten ordes específicas ou comportándose convencionalmente en
       caso contrario */
 
    public String orde()    { return (_orde != null ? _orde : super.orde()); }
 
    public String toString() { return ("Coronel " + super.toString()); }
 
    private String _orde;
}
/**
 * Esta clase é unha extensión instanciable da superclase Unidade que respecta o
 * comportamento por defecto da cadea de responsabilidade
 */
 
public class Soldado extends Unidade {
 
    // o construtor só ten que iniciar a parte correspondente á superclase
 
    public Soldado(String nome) {
        super(nome);
    }
 
    public String toString() { return ("Soldado " + super.toString()); }
}
SecuenciaGL.jpg