Cadena de responsabilitat
La Cadena de responsabilitat (Chain of Responsibility) és un patró de comportament dintre dels patrons de disseny en enginyeria de programari.
La seva principal finalitat és evitar una relació directa preestablerta entre un emissor d'una petició i el seu receptor final. D'aquesta manera es proporciona a més d'un objecte la possibilitat de tractar una petició.
Consta d'una estructura en cadena, on existeix un objecte (client) que genera una petició i els objectes receptors (Manipuladors) encadenats es passen la petició fins que és processada per algun d'aquests.
Estructura
[modifica]Participants
[modifica]- Manipulador:
- Defineix una interfície per tractar les peticions.[1]
- (opcional) Implementa l'enllaç al successor.[1]
- ManipuladorConcret
- Tracta les peticions de les quals és responsable.[1]
- Pot accedir al successor.[1]
- Si el ManipuladorConcret pot manegar la petició, ho fa. En cas contrari ho reenvia al seu successor.[1]
- Client
- Inicialitza la petició a un objecte ManipuladorConcret de la cadena.[1]
Conseqüències
[modifica]- Redueix l'acoblament: Ni el receptor ni l'emissor es coneixen explícitament entre ells. I un objecte de la cadena tampoc ha de conèixer l'estructura d'aquesta.[2]
- Afegeix flexibilitat per assignar responsabilitats a objectes.[2]
- No és garantida la recepció: Donat que les peticions no tenen un receptor explícit, no hi ha garantia que siguin manipulades. Una petició pot quedar sense tractar quan la cadena no està ben configurada.[2]
Aplicabilitat
[modifica]- En els casos en què hi ha més d'un objecte que pot manipular una petició i el manipulador no es coneix “a priori”.[1]
- Es vol enviar una petició entre diversos objectes sense especificar explícitament el receptor.[1]
- En el cas en què el conjunt d'objectes que conformen la cadena es defineixen dinàmicament.[1]
Utilitzacions conegudes
[modifica]- MacApp i ETT++ hi fan menció però amb el nom de “EventHandler”.[3]
- En la biblioteca de TCL de symantec es coneix amb el nom de ‘Bureaucrat’.[3]
- AppKit de NeXT utilitza el nom “Responder”.[3]
Exemple en Java
[modifica]En aquest exemple es reflecteix l'ús del patró per les 4 operacions aritmètiques bàsiques, on la primera operació rep un objecte que conté dos valors numèrics i una operació a realitzar. L'objecte es va propagant per la cadena fins que troba l'operació que la pot executar.
Diagrama de classes
[modifica]Codi
[modifica]public interface IEslavoCadena {
public void afegirSeguentEslavo(IEslavoCadena seguent);
public void calcular(Numero n);
}
Objecte a propagar.
public class Numero {
private final int n1;
private final int n2;
private final String operacio;
public Numero(int n1, int n2, String operacio){
this.n1 = n1;
this.n2 = n2;
this.operacio = operacio;
}
public int obtenirN1(){
return n1;
}
public int obtenirN2(){
return n2;
}
public String obtenirOperacio(){
return operacio;
}
}
Operacions bàsiques.
public class Suma implements IEslavoCadena{
private IEslavoCadena seguent;
@Override
public void afegirSeguentEslavo(IEslavoCadena seguent) {
this.seguent = seguent;
}
@Override
public void calcular(Numero n) {
if(n.obtenirOperacio().equalsIgnoreCase("sumar")){
System.out.printf("Resultat de la suma %d\n", (n.obtenirN1()+n.obtenirN2()));
}
else if(seguent != null){
seguent.calcular(n);
}
else{
throw new RuntimeException("No hi ha següent");
}
}
}
public class Resta implements IEslavoCadena{
private IEslavoCadena seguent;
@Override
public void afegirSeguentEslavo(IEslavoCadena seguent) {
this.seguent = seguent;
}
@Override
public void calcular(Numero n) {
if(n.obtenirOperacio().equalsIgnoreCase("resta")){
System.out.printf("Resultat de la resta %d\n", (n.obtenirN1()-n.obtenirN2()));
}
else if(seguent != null){
seguent.calcular(n);
}
else{
throw new RuntimeException("No hi ha següent");
}
}
}
public class Producte implements IEslavoCadena{
private IEslavoCadena seguent;
@Override
public void afegirSeguentEslavo(IEslavoCadena seguent) {
this.seguent = seguent;
}
@Override
public void calcular(Numero n) {
if(n.obtenirOperacio().equalsIgnoreCase("producte")){
System.out.printf("Resultat del producte %d\n", (n.obtenirN1()*n.obtenirN2()));
}
else if(seguent != null){
seguent.calcular(n);
}
else{
throw new RuntimeException("No hi ha següent");
}
}
}
public class Divisio implements IEslavoCadena{
private IEslavoCadena seguent;
@Override
public void afegirSeguentEslavo(IEslavoCadena seguent) {
this.seguent = seguent;
}
@Override
public void calcular(Numero n) {
if(n.obtenirN2() == 0) throw new RuntimeException("Intent de divisió per 0");
if(n.obtenirOperacio().equalsIgnoreCase("divisio")){
System.out.printf("Resultat de la divisio %d\n", (n.obtenirN1()/n.obtenirN2()));
}
else if(seguent != null){
seguent.calcular(n);
}
else{
throw new RuntimeException("No hi ha següent");
}
}
}
Programa principal.
public class Main{
public static void main(String[] args) {
IEslavoCadena suma = new Suma();
IEslavoCadena resta = new Resta();
IEslavoCadena producte = new Producte();
IEslavoCadena divisio = new Divisio();
Numero n1 = new Numero(5, 5, "producte");
Numero n2 = new Numero(15, 3, "divisio");
suma.afegirSeguentEslavo(resta);
resta.afegirSeguentEslavo(producte);
producte.afegirSeguentEslavo(divisio);
suma.calcular(n1);
suma.calcular(n2);
}
}