Chain-of-Responsibility-Muster

Das Chain-of-Responsibility-Muster erstellt eine Kette von Empfängerobjekten für eine Anfrage. Dieses Muster entkoppelt den Sender und den Empfänger der Anfrage anhand des Typs der Anfrage. Diese Art von Entwurfsmuster gehört zu den Verhaltensmustern.

Das Chain-of-Responsibility-Muster verbindet mehrere Handler (verarbeitende Objekte) in einer Kettenstruktur, sodass die Anfrage entlang der Kette weitergegeben wird, bis ein Handler sie verarbeitet.

Das Chain-of-Responsibility-Muster ermöglicht es mehreren Objekten, die Anfrage zu bearbeiten, wodurch eine Kopplung zwischen dem Sender und dem Empfänger der Anfrage vermieden wird. Diese Objekte werden zu einer Kette verbunden, und die Anfrage wird entlang der Kette weitergegeben.

Einführung

Absicht

Ermöglicht, dass die Anfrage entlang der Handler-Kette weitergegeben wird, bis die Anfrage behandelt wird.

Wesentliche gelöste Probleme

  • Entkoppelt Sender und Empfänger der Anfrage, sodass mehrere Objekte die Möglichkeit haben, die Anfrage zu empfangen, während der Sender nicht wissen muss, welches Objekt sie behandeln wird.

Anwendungsfall

  • Wenn mehrere Objekte eine Anfrage behandeln können und zur Laufzeit bestimmt wird, welches konkrete Objekt sie behandelt.
  • Wenn Sie eine Anfrage an eines von mehreren Objekten übermitteln müssen, ohne den Empfänger explizit anzugeben.

Implementierungsansatz

  • Handler-Schnittstelle definieren: Alle Handler müssen dieselbe Schnittstelle implementieren.
  • Konkrete Handler erstellen: Konkrete Klassen, die die Schnittstelle implementieren, mit Logik zur Anfragebehandlung und einer Referenz auf den nächsten Handler in der Kette.

Schlüsselcode

  • Handler-Schnittstelle: Eine Methode zur Behandlung von Anfragen definieren.
  • ConcreteHandler-Klasse: Implementiert die Handler-Schnittstelle, enthält Logik zur Anfragebehandlung und eine Referenz auf den nächsten Handler.

Anwendungsbeispiel

  1. Hot Potato: Weitergabeverhalten in einem Spiel, bis die Musik stoppt.
  2. Event-Bubbling: In JavaScript beginnen Ereignisse beim spezifischsten Element und breiten sich Ebene für Ebene nach oben aus.
  3. WebserverBeispiele hierfür sind Apache Tomcat bei der Zeichenkodierung, Struts2-Interceptors und Servlet-Filter.

Vorteile

  1. Kopplung reduzieren: Sender und Empfänger entkoppeln.
  2. Objekte vereinfachen: Das Objekt muss die Struktur der Kette nicht kennen.
  3. Flexibilität: Verantwortlichkeiten dynamisch hinzufügen oder entfernen, indem die Mitglieder oder die Reihenfolge der Kette geändert werden.
  4. Leicht erweiterbar: Es ist bequem, neue Request-Handler-Klassen hinzuzufügen.

Nachteile

  1. Anfrage nicht bearbeitet: Es gibt keine Garantie, dass die Anfrage von einem Handler in der Kette empfangen wird.
  2. Auswirkungen auf die Leistung: Kann die Systemleistung beeinträchtigen, und Debugging ist schwierig, kann zu zirkulären Aufrufen führen.
  3. Schwer zu beobachten: Laufzeiteigenschaften sind nicht offensichtlich, was das Debugging behindern kann.

Verwendungshinweise

  • Wenn bei der Bearbeitung einer Anfrage mehrere potenzielle Handler vorhanden sind, sollte man die Verwendung des Chain-of-Responsibility-Musters in Betracht ziehen.
  • Stellen Sie sicher, dass jeder Handler in der Kette genau weiß, wie die Anfrage an das nächste Glied in der Kette weitergegeben wird.

Hinweise

  • In der Java-Web-Entwicklung wird das Chain-of-Responsibility-Muster häufig verwendet, beispielsweise Filterketten, Interceptoren usw.

Struktur

Es umfasst hauptsächlich die folgenden Kernrollen:

  • Abstrakter Handler:

    • Definiert eine Schnittstelle zur Bearbeitung von Anfragen, die üblicherweise eine Methode zur Bearbeitung der Anfrage enthält (wie z. B.handleRequest) und eine Referenz auf den nächsten Handler (Nachfolger).
  • Konkreter Handler:

    • Implementiert die abstrakte Handler-Schnittstelle und ist für die Bearbeitung von Anfragen verantwortlich. Wenn er die Anfrage bearbeiten kann, bearbeitet er sie direkt; andernfalls leitet er die Anfrage an den nächsten Handler weiter.
  • Client:

    • Erstellen Sie Handler-Objekte und verbinden Sie sie zu einer Verantwortungskette. Normalerweise muss der Client die Anfrage nur an den ersten Handler in der Kette senden und muss sich nicht um die konkrete Verarbeitung der Anfrage kümmern.

Implementierung

Wir erstellen eine abstrakte KlasseAbstractLoggermit detaillierten Protokollierungsstufen. Dann erstellen wir drei Arten von Loggern, die alleAbstractLoggererweitern. Jeder Logger prüft, ob die Stufe der Nachricht zu seiner eigenen Stufe gehört; wenn ja, gibt er sie entsprechend aus; andernfalls gibt er sie nicht aus und leitet die Nachricht an den nächsten Logger weiter.

责任链模式的 UML 图

Schritt 1

Erstellen Sie eine abstrakte Logger-Klasse.

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //Das nächste Element in der Verantwortungskette protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }

Schritt 2

Erstellen Sie Entitätsklassen, die die Logger-Klasse erweitern.

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Standard-Konsole::Logger: " + message); } }

ErrorLogger.java

public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Fehler-Konsole::Logger: " + message); } }

FileLogger.java

public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }

Schritt 3

Erstellen Sie verschiedene Arten von Loggern. Weisen Sie ihnen unterschiedliche Fehlerstufen zu und setzen Sie den nächsten Logger in jedem Logger. Der nächste Logger in jedem Logger stellt einen Teil der Kette dar.

ChainPatternDemo.java

public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "Dies ist eine Information."); loggerChain.logMessage(AbstractLogger.DEBUG, "Dies ist eine Debug-Level-Information."); loggerChain.logMessage(AbstractLogger.ERROR, "Dies ist eine Fehlerinformation."); } }

Schritt 4

Führen Sie das Programm aus, Ausgabeergebnisse:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.
Weitere Erweiterungen