Chain of Responsibility Design Pattern in Java

Die Chain of Responsibility-Entwurfsmuster ist eines der Verhaltensmuster.

Chain of Responsibility-Entwurfsmuster

Das Chain of Responsibility-Muster wird verwendet, um eine lose Kopplung in der Softwareentwicklung zu erreichen, bei der eine Anfrage vom Client an eine Kette von Objekten weitergeleitet wird, um sie zu verarbeiten. Das Objekt in der Kette entscheidet dann selbst, welches Objekt die Anfrage verarbeiten wird und ob die Anfrage an das nächste Objekt in der Kette gesendet werden muss oder nicht.

Beispiel für das Chain of Responsibility-Muster in JDK

Lassen Sie uns das Beispiel des Chain-of-Responsibility-Musters in JDK ansehen und dann werden wir ein reales Beispiel für die Implementierung dieses Musters erstellen. Wir wissen, dass wir in einem try-catch-Block mehrere catch-Blöcke haben können. Jeder catch-Block ist sozusagen ein Prozessor, um diese bestimmte Ausnahme zu verarbeiten. Wenn also eine Ausnahme im try-Block auftritt, wird sie an den ersten catch-Block zur Verarbeitung gesendet. Wenn der catch-Block sie nicht verarbeiten kann, leitet er die Anforderung an das nächste Objekt in der Kette weiter, d. h. den nächsten catch-Block. Wenn selbst der letzte catch-Block sie nicht verarbeiten kann, wird die Ausnahme außerhalb der Kette an das aufrufende Programm weitergegeben.

Beispiel für das Designmuster „Chain of Responsibility“

Eines der großartigen Beispiele für das Muster der Kettenverantwortung ist der Geldautomat. Der Benutzer gibt den Betrag ein, der ausgegeben werden soll, und der Automat gibt den Betrag in Form von definierten Währungsscheinen wie 50 $, 20 $, 10 $ usw. aus. Wenn der Benutzer einen Betrag eingibt, der keine Vielfachen von 10 ist, wird ein Fehler ausgegeben. Wir werden das Muster der Kettenverantwortung verwenden, um diese Lösung umzusetzen. Die Kette wird die Anfrage in derselben Reihenfolge verarbeiten wie im folgenden Bild dargestellt. Beachten Sie, dass wir diese Lösung problemlos in einem einzigen Programm selbst implementieren können, aber dann würde die Komplexität zunehmen und die Lösung wäre stark gekoppelt. Daher werden wir eine Kette von Ausgabesystemen erstellen, um Scheine von 50 $, 20 $ und 10 $ auszugeben.

Muster des Kettenverantwortungsdesigns – Basisklassen und Schnittstelle

Wir können eine Klasse Währung erstellen, die den auszugebenden Betrag speichert und von den Kettenimplementierungen verwendet wird. Währung.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;
	}
}

Die Basisschnittstelle sollte eine Methode haben, um den nächsten Prozessor in der Kette zu definieren, und die Methode, die die Anfrage verarbeitet. Unsere ATM-Ausgabe-Schnittstelle wird wie folgt aussehen. AusgabeKette.java

package com.journaldev.design.chainofresponsibility;

public interface DispenseChain {

	void setNextChain(DispenseChain nextChain);
	
	void dispense(Currency cur);
}

Chain of Responsibilities Muster – Implementierung der Ketten

Wir müssen verschiedene Prozessorklassen erstellen, die das Interface DispenseChain implementieren und die Implementierung der Dispense-Methoden bereitstellen. Da wir unser System entwickeln, um mit drei Arten von Währungsscheinen zu arbeiten – 50 $, 20 $ und 10 $, erstellen wir drei konkrete Implementierungen. 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);
		}
	}

}

Der wichtige Punkt hier ist die Implementierung der Dispense-Methode. Sie werden feststellen, dass jede Implementierung versucht, die Anfrage zu verarbeiten und basierend auf dem Betrag einen Teil oder den gesamten Betrag verarbeiten kann. Wenn eine der Ketten dies nicht vollständig verarbeiten kann, sendet sie die Anfrage an den nächsten Prozessor in der Kette, um den verbleibenden Teil zu verarbeiten. Wenn der Prozessor nichts verarbeiten kann, leitet er die gleiche Anfrage einfach an die nächste Kette weiter.

Chain of Responsibilities Design Pattern – Erstellen der Kette

Dies ist ein sehr wichtiger Schritt, und wir sollten die Kette sorgfältig erstellen, da sonst ein Prozessor möglicherweise überhaupt keine Anfragen erhält. Wenn wir beispielsweise in unserer Implementierung die erste Prozessorkette als Dollar10Dispenser und dann Dollar20Dispenser halten, wird die Anfrage nie an den zweiten Prozessor weitergeleitet, und die Kette wird nutzlos. Hier ist unsere ATM-Dispenser-Implementierung zur Verarbeitung des vom Benutzer angeforderten Betrags. ATMDispenseChain.java

package com.journaldev.design.chainofresponsibility;

import java.util.Scanner;

public class ATMDispenseChain {

	private DispenseChain c1;

	public ATMDispenseChain() {
		// initialisieren der kette
		this.c1 = new Dollar50Dispenser();
		DispenseChain c2 = new Dollar20Dispenser();
		DispenseChain c3 = new Dollar10Dispenser();

		// setzen der verantwortungskette
		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;
			}
			// anfrage verarbeiten
			atmDispenser.c1.dispense(new Currency(amount));
		}

	}

}

Wenn wir die obige Anwendung ausführen, erhalten wir eine Ausgabe wie unten gezeigt.

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.

Klassen Diagramm des Chain of Responsibilities Design Patterns

Unser Beispiel für das ATM-Ausgabe-System mit dem Designmuster der Verantwortungskette sieht wie das folgende Bild aus.

Wichtige Punkte des Designmusters Verantwortungskette

  • Der Client weiß nicht, welcher Teil der Kette die Anfrage bearbeiten wird, und er sendet die Anfrage an das erste Objekt in der Kette. Zum Beispiel ist in unserem Programm „ATMDispenseChain“ nicht bekannt, wer die Anfrage zur Auszahlung des eingegebenen Betrags bearbeitet.
  • Jedes Objekt in der Kette hat seine eigene Implementierung, um die Anfrage entweder vollständig oder teilweise zu bearbeiten oder sie an das nächste Objekt in der Kette weiterzuleiten.
  • Jedes Objekt in der Kette sollte eine Referenz auf das nächste Objekt in der Kette haben, um die Anfrage weiterzuleiten. Dies wird durch die Verwendung von Java-Komposition erreicht.
  • Es ist sehr wichtig, die Kette sorgfältig zu erstellen, da es sonst passieren kann, dass die Anfrage niemals an einen bestimmten Prozessor weitergeleitet wird oder keine Objekte in der Kette vorhanden sind, die die Anfrage bearbeiten können. In meiner Implementierung habe ich die Überprüfung des vom Benutzer eingegebenen Betrags hinzugefügt, um sicherzustellen, dass er von allen Prozessoren vollständig verarbeitet wird. Es könnte jedoch sein, dass wir dies nicht überprüfen und eine Ausnahme werfen, wenn die Anfrage das letzte Objekt erreicht und keine weiteren Objekte in der Kette vorhanden sind, an die die Anfrage weitergeleitet werden kann. Dies ist eine Designentscheidung.
  • Das Designmuster „Chain of Responsibility“ ist gut geeignet, um lose Kopplung zu erreichen, aber es geht mit dem Kompromiss einher, dass es viele Implementierungsklassen und Wartungsprobleme gibt, wenn der meiste Code in allen Implementierungen gleich ist.

Chain of Responsibility Pattern Beispiele in JDK

  • java.util.logging.Logger#log()
  • javax.servlet.Filter#doFilter()

Das ist alles für das Designmuster „Chain of Responsibility“. Ich hoffe, es hat Ihnen gefallen und konnte Ihr Verständnis für dieses Designmuster klären.

Source:
https://www.digitalocean.com/community/tutorials/chain-of-responsibility-design-pattern-in-java