Chaîne de responsabilité est l’un des modèles de conception comportementaux.
Modèle de conception de la chaîne de responsabilité
Le modèle de la chaîne de responsabilité est utilisé pour réaliser un couplage lâche dans la conception logicielle, où une demande du client est transmise à une chaîne d’objets pour les traiter. Ensuite, l’objet dans la chaîne décidera lui-même qui traitera la demande et si la demande doit être envoyée à l’objet suivant dans la chaîne ou non.
Exemple de modèle de chaîne de responsabilité dans JDK
Voyons l’exemple du modèle de conception de chaîne de responsabilité dans JDK, puis nous passerons à la mise en œuvre d’un exemple réel de ce modèle. Nous savons que nous pouvons avoir plusieurs blocs catch dans un bloc de code try-catch. Ici, chaque bloc catch est une sorte de processeur pour traiter cette exception particulière. Ainsi, lorsque n’importe quelle exception se produit dans le bloc try, elle est envoyée au premier bloc catch pour traitement. Si le bloc catch n’est pas en mesure de le traiter, il transmet la demande à l’objet suivant dans la chaîne, c’est-à-dire au bloc catch suivant. Si même le dernier bloc catch n’est pas en mesure de le traiter, l’exception est lancée en dehors de la chaîne vers le programme appelant.
Exemple de modèle de conception de chaîne de responsabilité
L’un des grands exemples du modèle de conception de la Chaîne de Responsabilité est le distributeur automatique de billets (ATM Dispense machine). L’utilisateur entre le montant à distribuer, et la machine délivre la somme en billets de devise prédéfinie tels que 50 $, 20 $, 10 $, etc. Si l’utilisateur entre un montant qui n’est pas un multiple de 10, une erreur est générée. Nous utiliserons le modèle de conception de la Chaîne de Responsabilité pour mettre en œuvre cette solution. La chaîne traitera la demande dans le même ordre que l’image ci-dessous. Notez que nous pouvons facilement mettre en œuvre cette solution dans un seul programme, mais la complexité augmentera et la solution sera fortement couplée. Nous allons donc créer une chaîne de systèmes de distribution pour distribuer des billets de 50 $, 20 $ et 10 $.
Modèle de conception de la Chaîne de Responsabilité – Classes de base et Interface
Nous pouvons créer une classe Currency
qui stockera le montant à distribuer et qui sera utilisée par les implémentations de la chaîne. Currency.java
package com.journaldev.design.chainofresponsibility;
public class Currency {
private int amount;
public Currency(int amt){
this.amount=amt;
}
public int getAmount(){
return this.amount;
}
}
L’interface de base devrait avoir une méthode pour définir le processeur suivant dans la chaîne et la méthode qui traitera la demande. Notre interface de distributeur automatique de billets ressemblera à ce qui suit. DispenseChain.java
package com.journaldev.design.chainofresponsibility;
public interface DispenseChain {
void setNextChain(DispenseChain nextChain);
void dispense(Currency cur);
}
Modèle de conception de chaîne de responsabilité – Implémentations en chaîne
Nous devons créer différentes classes de processeur qui implémenteront l’interface DispenseChain
et fourniront une implémentation des méthodes de distribution. Comme nous développons notre système pour fonctionner avec trois types de billets de monnaie – 50 $, 20 $ et 10 $, nous créerons trois implémentations concrètes. Dollar50Dispenser.java
package com.journaldev.design.chainofresponsibility;
public class Dollar50Dispenser implements DispenseChain {
private DispenseChain chain;
@Override
public void setNextChain(DispenseChain nextChain) {
this.chain=nextChain;
}
@Override
public void dispense(Currency cur) {
if(cur.getAmount() >= 50){
int num = cur.getAmount()/50;
int remainder = cur.getAmount() % 50;
System.out.println("Dispensing "+num+" 50$ note");
if(remainder !=0) this.chain.dispense(new Currency(remainder));
}else{
this.chain.dispense(cur);
}
}
}
Dollar20Dispenser.java
package com.journaldev.design.chainofresponsibility;
public class Dollar20Dispenser implements DispenseChain{
private DispenseChain chain;
@Override
public void setNextChain(DispenseChain nextChain) {
this.chain=nextChain;
}
@Override
public void dispense(Currency cur) {
if(cur.getAmount() >= 20){
int num = cur.getAmount()/20;
int remainder = cur.getAmount() % 20;
System.out.println("Dispensing "+num+" 20$ note");
if(remainder !=0) this.chain.dispense(new Currency(remainder));
}else{
this.chain.dispense(cur);
}
}
}
Dollar10Dispenser.java
package com.journaldev.design.chainofresponsibility;
public class Dollar10Dispenser implements DispenseChain {
private DispenseChain chain;
@Override
public void setNextChain(DispenseChain nextChain) {
this.chain=nextChain;
}
@Override
public void dispense(Currency cur) {
if(cur.getAmount() >= 10){
int num = cur.getAmount()/10;
int remainder = cur.getAmount() % 10;
System.out.println("Dispensing "+num+" 10$ note");
if(remainder !=0) this.chain.dispense(new Currency(remainder));
}else{
this.chain.dispense(cur);
}
}
}
Le point important à noter ici est la mise en œuvre de la méthode de distribution. Vous remarquerez que chaque implémentation tente de traiter la demande et, en fonction du montant, elle peut traiter tout ou une partie de celui-ci. Si l’une des chaînes ne parvient pas à le traiter entièrement, elle envoie la demande au processeur suivant dans la chaîne pour traiter le reste de la demande. Si le processeur ne parvient pas à traiter quoi que ce soit, il transmet simplement la même demande à la chaîne suivante.
Modèle de conception de chaîne de responsabilité – Création de la chaîne
C’est une étape très importante et nous devrions créer la chaîne avec soin, sinon un processeur pourrait ne recevoir aucune demande du tout. Par exemple, dans notre implémentation, si nous gardons la première chaîne de processeurs comme Dollar10Dispenser
puis Dollar20Dispenser
, alors la demande ne sera jamais transmise au deuxième processeur et la chaîne deviendra inutile. Voici notre implémentation du distributeur de billets pour traiter le montant demandé par l’utilisateur. ATMDispenseChain.java
package com.journaldev.design.chainofresponsibility;
import java.util.Scanner;
public class ATMDispenseChain {
private DispenseChain c1;
public ATMDispenseChain() {
// initialiser la chaîne
this.c1 = new Dollar50Dispenser();
DispenseChain c2 = new Dollar20Dispenser();
DispenseChain c3 = new Dollar10Dispenser();
// définir la chaîne de responsabilité
c1.setNextChain(c2);
c2.setNextChain(c3);
}
public static void main(String[] args) {
ATMDispenseChain atmDispenser = new ATMDispenseChain();
while (true) {
int amount = 0;
System.out.println("Enter amount to dispense");
Scanner input = new Scanner(System.in);
amount = input.nextInt();
if (amount % 10 != 0) {
System.out.println("Amount should be in multiple of 10s.");
return;
}
// traiter la demande
atmDispenser.c1.dispense(new Currency(amount));
}
}
}
Lorsque nous exécutons l’application ci-dessus, nous obtenons une sortie comme ci-dessous.
Enter amount to dispense
530
Dispensing 10 50$ note
Dispensing 1 20$ note
Dispensing 1 10$ note
Enter amount to dispense
100
Dispensing 2 50$ note
Enter amount to dispense
120
Dispensing 2 50$ note
Dispensing 1 20$ note
Enter amount to dispense
15
Amount should be in multiple of 10s.
Diagramme de classe du modèle de conception de la chaîne de responsabilité
Notre exemple de distributeur automatique de billets pour la mise en œuvre du modèle de conception de la chaîne de responsabilité ressemble à l’image ci-dessous.
Points Importants du Modèle de Conception de la Chaîne de Responsabilité
- Le client ne sait pas quelle partie de la chaîne traitera la demande et enverra la demande au premier objet de la chaîne. Par exemple, dans notre programme ATMDispenseChain, il est inconscient de qui traite la demande pour distribuer le montant saisi.
- Chaque objet de la chaîne aura sa propre implémentation pour traiter la demande, que ce soit intégralement, partiellement ou pour l’envoyer à l’objet suivant dans la chaîne.
- Chaque objet de la chaîne devrait avoir une référence vers l’objet suivant dans la chaîne pour transmettre la demande, cela est réalisé par la composition en Java.
- Créer la chaîne avec soin est très important, sinon il pourrait arriver que la demande ne soit jamais transmise à un processeur particulier ou qu’il n’y ait aucun objet dans la chaîne capable de traiter la demande. Dans mon implémentation, j’ai ajouté la vérification pour le montant saisi par l’utilisateur pour m’assurer qu’il est traité entièrement par tous les processeurs, mais nous pourrions ne pas le vérifier et générer une exception si la demande atteint le dernier objet et qu’il n’y a pas d’autres objets dans la chaîne pour transmettre la demande. Il s’agit d’une décision de conception.
- Le modèle de conception de la chaîne de responsabilité est bon pour obtenir un couplage lâche, mais il a pour contrepartie d’avoir beaucoup de classes d’implémentation et des problèmes de maintenance si une grande partie du code est commune à toutes les implémentations.
Exemples de motif de chaîne de responsabilité dans JDK
- java.util.logging.Logger#log()
- javax.servlet.Filter#doFilter()
Voilà tout pour le motif de conception de la chaîne de responsabilité, j’espère que vous l’avez apprécié et qu’il a pu éclaircir votre compréhension de ce motif de conception.
Source:
https://www.digitalocean.com/community/tutorials/chain-of-responsibility-design-pattern-in-java