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

Cadea de Responsabilidade (en inglés "Chain of Responsability") é un padrón de deseño [1]que permite xestionar unha petición a máis dun 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 padró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

  • 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 padró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.
 

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.

 

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.

 

Aplicabilidade editar

  • Cando hai máis dun 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

 

Participantes editar

  • 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

  • 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

  • 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

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 padró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.

padróns relacionados editar

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

Exemplo de implementación editar

 
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()); }
}

 
  1. "Libro moderno sobre patrones de diseño: Sumérgete en los patrones de diseño". refactoring.guru (en castelán). Consultado o 2023-08-04.