1. Was ist das Chain of Responsibility Pattern

Das Chain of Responsibility Pattern ist ein Verhaltensmuster des Entwurfs, das den Sender und Empfänger einer Anforderung entkoppelt und mehreren Objekten die Möglichkeit gibt, die Anforderung zu bearbeiten. Jeder Empfänger enthält einen Verweis auf einen anderen Empfänger und leitet die Anforderung an den nächsten Empfänger weiter, wenn er die Anforderung nicht bearbeiten kann, bis die Anforderung bearbeitet ist oder das Ende der Kette erreicht ist.

2. Merkmale und Vorteile des Chain of Responsibility Pattern

Die Merkmale und Vorteile des Chain of Responsibility Pattern sind wie folgt:

  • Entkopplung von Sender und Empfänger: Der Sender muss sich nicht darum kümmern, welcher Empfänger die Anfrage bearbeitet, noch muss er die spezifischen Handler in der Kette kennen.
  • Flexibilität: Es ermöglicht die dynamische Hinzufügung, Entfernung oder Neuanordnung von Handlern in der Kette, ohne den Code des Senders und Empfängers zu ändern.
  • Erweiterbarkeit: Einfaches Erweitern der Verantwortungskette durch Hinzufügen neuer spezifischer Handler.
  • Single Responsibility Principle: Jeder spezifische Handler muss sich nur um seine eigene Bearbeitungslogik kümmern.
  • Konfigurierbarkeit: Die Kette von Handlern kann entsprechend den Anforderungen konfiguriert werden, sodass unterschiedliche Anfragen unterschiedliche Handlerketten haben können.

3. Beispiele für praktische Anwendungen des Chain of Responsibility Pattern

Das Chain of Responsibility Pattern hat viele praktische Anwendungen, wie zum Beispiel:

  • Behandlung von Anfragen in Webanwendungen: Es kann verwendet werden, um verschiedene Arten von Anfragen wie Identitätsauthentifizierung, Protokollierung und Berechtigungsüberprüfung zu behandeln.
  • Fehlerbehandlung: Es kann verwendet werden, um Fehler zu behandeln, wobei jeder Handler für die Behandlung eines bestimmten Fehlertyps verantwortlich ist und den Fehler bei Bedarf an den nächsten Handler weiterleitet.
  • Ereignisbehandlung: Es kann verwendet werden, um verschiedene Arten von Ereignissen wie Benutzerklickereignisse, Netzwerkanforderungsevents und so weiter zu behandeln.

4. Implementierung des Chain of Responsibility Pattern in Golang

4.1 UML-Klassendiagramm

Golang Chain of Responsibility Pattern

4.2 Beispiel Einführung

Im obigen UML-Klassendiagramm haben wir einen abstrakten Handler (Handler) und zwei konkrete Handler (ConcreteHandler1 und ConcreteHandler2) definiert. Der Client (Client) leitet Anfragen ein, indem er die Methode handleRequest des Handlers aufruft.

4.3 Implementierung Schritt 1: Definition des abstrakten Handler-Interfaces

type Handler interface {
    HandleRequest(request Request) error
    SetNext(handler Handler)
}

type Request interface {
    Condition bool
}

Das abstrakte Handler-Interface definiert die Methode HandleRequest zur Verarbeitung von Anfragen und die Methode SetNext zum Setzen des nächsten Handlers.

4.4 Implementierung Schritt 2: Implementierung konkreter Handlerklassen

type ConcreteHandler1 struct {
    next Handler
}

func (h *ConcreteHandler1) HandleRequest(request Request) error {
    // Logik zur Bearbeitung der Anfrage
    if request.Condition {
        // Code zur Behandlung der Anfrage
        return nil
    } else {
        if h.next != nil {
            return h.next.HandleRequest(request)
        }
        return errors.New("Kein Handler gefunden")
    }
}

func (h *ConcreteHandler1) SetNext(handler Handler) {
    h.next = handler
}

type ConcreteHandler2 struct {
    next Handler
}

func (h *ConcreteHandler2) HandleRequest(request Request) error {
    // Logik zur Bearbeitung der Anfrage
    if request.Condition {
        // Code zur Behandlung der Anfrage
        return nil
    } else {
        if h.next != nil {
            return h.next.HandleRequest(request)
        }
        return errors.New("Kein Handler gefunden")
    }
}

func (h *ConcreteHandler2) SetNext(handler Handler) {
    h.next = handler
}

Die konkreten Handlerklassen implementieren das abstrakte Handler-Interface und überschreiben die Methoden HandleRequest und SetNext.

4.5 Implementierung Schritt 3: Aufbau der Chain of Responsibility

handler1 := &ConcreteHandler1{}
handler2 := &ConcreteHandler2{}

handler1.SetNext(handler2)

Durch Instanziierung der konkreten Handler und Setzen des nächsten Handlers wird eine Chain of Responsibility aufgebaut.

4.6 Implementierung Schritt 4: Clientcode

func main() {
    handler := &ConcreteHandler1{}

    // Aufbau der Chain of Responsibility
    handler.SetNext(&ConcreteHandler2{})

    // Senden einer Anfrage
    handler.HandleRequest(Request{Condition: true})
}

Im Client-Code wird ein konkreter Handler instanziiert, der nächste Handler wird festgelegt und dann wird die Methode HandleRequest aufgerufen, um eine Anfrage zu senden.