Chain of responsibility ontwerppatroon is een van de gedragsontwerppatronen.
Chain of Responsibility Ontwerppatroon
Het chain of responsibility-patroon wordt gebruikt om losse koppeling te bereiken in softwareontwerp, waarbij een verzoek van de client wordt doorgegeven aan een keten van objecten om ze te verwerken. Vervolgens zal het object in de keten zelf beslissen wie het verzoek zal verwerken en of het verzoek al dan niet naar het volgende object in de keten moet worden gestuurd.
Voorbeeld van het Chain of Responsibility-patroon in JDK
Laten we het voorbeeld van het ketenverantwoordelijkheidsontwerp (chain of responsibility pattern) in JDK bekijken en vervolgens gaan we over tot het implementeren van een real-life voorbeeld van dit patroon. We weten dat we meerdere catch blocks kunnen hebben in een try-catch block code. Hier is elke catch block een soort verwerker om die specifieke uitzondering te verwerken. Dus wanneer er een uitzondering optreedt in het try-blok, wordt deze naar het eerste catch-blok gestuurd om te verwerken. Als het catch-blok het niet kan verwerken, stuurt het het verzoek door naar het volgende object in de keten, dat wil zeggen het volgende catch-blok. Als zelfs het laatste catch-blok het niet kan verwerken, wordt de uitzondering buiten de keten naar het aanroepende programma gegooid.
Voorbeeld van het ketenverantwoordelijkheidsontwerppatroon
Een van de grote voorbeelden van het Chain of Responsibility-patroon is de ATM-geldautomaat. De gebruiker voert het bedrag in dat moet worden uitgegeven en de machine geeft het bedrag uit in termen van gedefinieerde valutabiljetten zoals 50$, 20$, 10$ enzovoort. Als de gebruiker een bedrag invoert dat geen veelvoud is van 10, wordt er een foutmelding weergegeven. We zullen het Chain of Responsibility-patroon gebruiken om deze oplossing te implementeren. De keten zal het verzoek verwerken in dezelfde volgorde als de onderstaande afbeelding. Let op dat we deze oplossing gemakkelijk in een enkel programma kunnen implementeren, maar dan zal de complexiteit toenemen en zal de oplossing sterk gekoppeld zijn. Dus zullen we een keten van uitgiftesystemen maken om biljetten van 50$, 20$ en 10$ uit te geven.
Chain of Responsibility Ontwerppatroon – Basisklassen en Interface
We kunnen een klasse Valuta
maken die het te verstrekken bedrag zal opslaan en gebruikt zal worden door de ketenimplementaties. Valuta.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;
}
}
De basismethode moet een methode hebben om de volgende processor in de keten te definiëren en de methode die het verzoek zal verwerken. Onze ATM-uitgifte-interface zal er als volgt uitzien. DispenseChain.java
package com.journaldev.design.chainofresponsibility;
public interface DispenseChain {
void setNextChain(DispenseChain nextChain);
void dispense(Currency cur);
}
Verantwoordelijkheidsketenpatroon – Implementaties Keten
We moeten verschillende verwerkerklassen maken die de interface DispenseChain
zullen implementeren en de dispense-methoden zullen leveren. Aangezien we ons systeem ontwikkelen om te werken met drie soorten valutabiljetten – 50$, 20$ en 10$, zullen we drie concrete implementaties maken. 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);
}
}
}
Het belangrijke punt om hier op te merken is de implementatie van de dispense-methode. Je zult opmerken dat elke implementatie probeert het verzoek te verwerken en op basis van het bedrag mogelijk een deel ervan of het volledige deel ervan verwerkt. Als een van de ketens het niet volledig kan verwerken, stuurt het het verzoek naar de volgende verwerker in de keten om de rest van het verzoek te verwerken. Als de verwerker niets kan verwerken, stuurt hij hetzelfde verzoek eenvoudig door naar de volgende keten.
Verantwoordelijkheidsketen Ontwerppatroon – Het Creëren van de Keten
Dit is een zeer belangrijke stap en we moeten de keten zorgvuldig creëren, anders kan een processor helemaal geen verzoeken ontvangen. Als voorbeeld, in onze implementatie als we de eerste processor keten als Dollar10Dispenser
houden en dan Dollar20Dispenser
, dan zal het verzoek nooit worden doorgestuurd naar de tweede processor en wordt de keten nutteloos. Hier is onze ATM Dispenser implementatie om het gevraagde bedrag van de gebruiker te verwerken. ATMDispenseChain.java
package com.journaldev.design.chainofresponsibility;
import java.util.Scanner;
public class ATMDispenseChain {
private DispenseChain c1;
public ATMDispenseChain() {
// initialiseer de keten
this.c1 = new Dollar50Dispenser();
DispenseChain c2 = new Dollar20Dispenser();
DispenseChain c3 = new Dollar10Dispenser();
// stel de keten van verantwoordelijkheid in
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;
}
// verwerk het verzoek
atmDispenser.c1.dispense(new Currency(amount));
}
}
}
Wanneer we bovenstaande toepassing uitvoeren, krijgen we een output zoals hieronder.
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.
Keten van Verantwoordelijkheden Ontwerp Patroon Klassediagram
Ons voorbeeld van een ATM-uitgifteautomaat voor de implementatie van het keten van verantwoordelijkheidsontwerppatroon ziet eruit als onderstaande afbeelding.
Keten van Verantwoordelijkheid Ontwerp Patroon Belangrijke Punten
- De klant weet niet welk deel van de keten het verzoek zal verwerken en stuurt het verzoek naar het eerste object in de keten. Bijvoorbeeld, in ons programma ATMDispenseChain weet niet wie het verzoek verwerkt om het ingevoerde bedrag uit te betalen.
- Elk object in de keten zal zijn eigen implementatie hebben om het verzoek te verwerken, ofwel volledig of gedeeltelijk, of om het naar het volgende object in de keten te sturen.
- Elk object in de keten moet verwijzen naar het volgende object in de keten om het verzoek door te sturen, dit wordt bereikt door middel van Java-compositie.
- Het zorgvuldig maken van de keten is erg belangrijk, anders kan het zijn dat het verzoek nooit wordt doorgestuurd naar een bepaalde verwerker of dat er geen objecten in de keten zijn die het verzoek kunnen verwerken. In mijn implementatie heb ik de controle toegevoegd voor het door de gebruiker ingevoerde bedrag om ervoor te zorgen dat het volledig wordt verwerkt door alle verwerkers, maar we kunnen dit controleren en een uitzondering gooien als het verzoek het laatste object bereikt en er geen verdere objecten in de keten zijn om het verzoek naar door te sturen. Dit is een ontwerpbeslissing.
- De Chain of Responsibility-ontwerppatroon is goed om losse koppeling te bereiken, maar het gaat gepaard met het nadeel van het hebben van veel implementatieklassen en onderhoudsproblemen als het meeste code gemeenschappelijk is in alle implementaties.
Voorbeelden van de Chain of Responsibility-patroon in JDK
- java.util.logging.Logger#log()
- javax.servlet.Filter#doFilter()
Dat is alles voor het Chain of Responsibility-ontwerppatroon, ik hoop dat je het leuk vond en dat het je begrip van dit ontwerppatroon heeft verduidelijkt.
Source:
https://www.digitalocean.com/community/tutorials/chain-of-responsibility-design-pattern-in-java