Chain of Responsibility: Sauberer Code für chaotische Business-Logik
Lass mich von der Funktion erzählen, die mir den Schlaf geraubt hat.
Jedes System hat sie irgendwann: diese eine Funktion, die niemand anfassen will. Am Anfang ist sie harmlos – ein simpler Validierungscheck, ein `if` hier, noch einer da. Dann kommen Requirements dazu, Stakeholder wollen "nur eine kleine Ausnahme", und plötzlich starrst du auf ein 500-Zeilen-Monster, das aussieht, als hätte es ein Komitee geschrieben (weil es auch so war).
Genau hier rettet das Chain-of-Responsibility-Pattern den Tag – nicht mit Magie, sondern mit Disziplin. Lass mich zeigen, wie dieser klassische Design-Pattern deine verworrenste Business-Logik entwirrt.
Was ist Chain of Responsibility überhaupt?
Im Kern geht es darum, einen Request eine Kette von Handlern entlangzureichen, bis sich einer darum kümmert. Stell dir vor wie eine Eskalations-Kette im Kundenservice: Jeder versucht, dein Problem zu lösen. Klappt nicht? Weiter zum Nächsten.
In Code bedeutet das: Statt einer riesigen Methode, die jedes Szenario abdeckt, baust du eine Reihe kleiner, fokussierter Handler. Jeder verarbeitet den Request – oder reicht ihn einfach weiter.
Wo CoR in der Praxis richtig glänzt
E-Commerce Order Processing
Stell dir vor: Du baust einen Shop. Deine `processOrder()`-Funktion muss Rabattcodes, Treuepunkte, Saison-Aktionen, Firmenkonten, VIP-Status und Sonderpartnerschaften abdecken. Ergebnis: Hunderte Zeilen verschachtelter Conditionals.
Mit Chain of Responsibility erstellst du statt dessen einzelne Prozessoren:
```javascript
class DiscountCodeHandler {
handle(order) {
// Rabatt-Logik anwenden
return next ? next.handle(order) : order;
}
}
class LoyaltyPointsHandler {
handle(order) {
// Treuepunkte anwenden
return next ? next.handle(order) : order;
}
}
```
Jeder Handler kümmert sich um genau eine Sache. Testen und Warten? Ein Kinderspiel.
Content-Moderation-Pipeline
Social-Media-Plattformen haben komplexe Moderations-Workflows. Ein Post muss gegen Community-Guidelines geprüft werden, auf Hate Speech gescannt, auf politischen Content geflaggt, auf Copyright-Verletzungen gecheckt und auf Misinformation bewertet werden – alles *vor* der Veröffentlichung.
Anstatt alles in eine Moderations-Funktion zu quetschen, baust du eine Pipeline:
1. **Spam Detection Handler** – Filtert offensichtlichen Spam raus
2. **Hate Speech Handler** – Flaggt beleidigende Sprache
3. **Copyright Handler** – Prüft gegen bekannte urheberrechtlich geschützte Inhalte
4. **Political Content Handler** – Leitet sensible politische Posts weiter
5. **Final Review Handler** – Menschliche Moderation
Jeder Schritt erledigt seinen Job oder reicht einfach weiter.
Payment Authorization Flow
Payment-Verarbeitung hat mehrere Validierungs- und Freigabe-Ebenen. Statt eines monolithischen Payment-Processors:
1. **Fraud Detection Handler** – Blockt verdächtige Transaktionen
2. **Credit Limit Handler** – Prüft ausreichende Deckung
3. **Bank Verification Handler** – Bestätigt Konto-Gültigkeit
4. **Currency Conversion Handler** – Kümmerst sich um internationale Zahlungen
5. **Payment Gateway Handler** – Führt die eigentliche Transaktion aus
Neue Verifikationsschritte lassen sich so easy hinzufügen, ohne bestehenden Code anzufassen.
Deine erste Chain implementieren
Hier eine praktische Implementation in JavaScript:
```javascript
// Base Handler Klasse
class BaseHandler {
constructor() {
this.nextHandler = null;
}
setNext(handler) {
this.nextHandler = handler;
return handler;
}
handle(request) {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null; // Kein Handler hat den Request verarbeitet
}
}
// Konkrete Handler
class AuthenticationHandler extends BaseHandler {
handle(request) {
if (!request.user) {
return { error: 'Authentication required' };
}
console.log('Authentication passed');
return super.handle(request);
}
}
class AuthorizationHandler extends BaseHandler {
handle(request) {
if (request.user.role !== 'admin') {
return { error: 'Insufficient permissions' };
}
console.log('Authorization passed');
return super.handle(request);
}
}
class ValidationHandler extends BaseHandler {
handle(request) {
if (!request.data || Object.keys(request.data).length === 0) {
return { error: 'Invalid data' };
}
console.log('Validation passed');
return super.handle(request);
}
}
// Usage
const authHandler = new AuthenticationHandler();
const authzHandler = new AuthorizationHandler();
const validationHandler = new ValidationHandler();
authHandler.setNext(authzHandler).setNext(validationHandler);
const result = authHandler.handle({
user: { role: 'admin' },
data: { amount: 100 }
});
```
Vorteile, die wirklich zählen
**Wartbarkeit**: Jeder Handler macht eine Sache – und die gut. Ändern sich Business-Regeln, fasst du nur den relevanten Handler an.
**Testbarkeit**: Unit-Tests werden easy – du testest jeden Handler isoliert, statt komplexe Szenarien zu mocken.
**Flexibilität**: Processing-Reihenfolge ändern? Einfach die Chain-Konfiguration anpassen. Conditional Routing gewünscht? Custom-Logik in den Handlern implementieren.
**Debugging**: Wenn was schiefgeht, weißt du sofort, welcher Handler versagt hat – weil jeder seine Entscheidung loggt.
Fallstricke, die du vermeiden solltest
Mach Handler nicht zu generisch – aber auch nicht zu spezifisch. Die Balance: Jeder Handler repräsentiert eine sinnvolle Business-Regel. Und: Lass Handler nicht wissen, wo sie in der Kette stehen. Sie sollen sich auf ihre Verantwortung konzentrieren und der Chain vertrauen.
Wichtig: Chain of Responsibility ersetzt nicht *all* deine Conditional Logic. Es geht darum, komplexe Workflows zu organisieren, wo mehrere distincte Processing-Schritte nacheinander laufen müssen.
Tools und Frameworks, die CoR leben
Viele moderne Frameworks nutzen Varianten dieses Patterns:
- **Express.js Middleware** ([expressjs.com](https://expressjs.com)) arbeitet chain-artig
- **ASP.NET Core Middleware Pipeline** folgt ähnlichen Prinzipien
- **Java Servlet Filters** implementieren Chain Processing
Selbst wenn dein Framework CoR nicht explizit nutzt: Das Pattern zu verstehen hilft dir, bessere Middleware- und Interceptor-Architekturen zu designen.
Wann du Chain of Responsibility NICHT nutzen solltest
CoR ist kein Allheilmittel. Finger weg, wenn:
- Processing-Schritte eng gekoppelt sind und immer zusammen laufen
- Performance kritisch ist und du Method-Call-Overhead minimieren musst
- Die Verarbeitungsreihenfolge egal oder fix ist
- Du weniger als drei distinkte Processing-Schritte hast
In deinem Codebase durchstarten
Bereit, Chain of Responsibility in dein Projekt zu holen? Fang klein an:
1. Identifiziere eine komplexe Funktion, die niemand anfassen will
2. Zerlege sie in logische Processing-Schritte
3. Erstelle separate Handler-Klassen für jeden Schritt
4. Verdrahte sie zur Chain
5. Test gründlich, bevor du auf andere Bereiche ausweitest
Der Aufwand zahlt sich schnell aus – weniger Debugging-Zeit, sauberere Code-Reviews.
FAQ
**F: Wie behandle ich Fehler in der Chain?**
A: Jeder Handler sollte den Request entweder erfolgreich verarbeiten oder eine passende Exception werfen. Überleg dir einen Error-Handler am Ende der Chain, der unverarbeitete Requests abfängt.
**F: Können Handler den Request modifizieren, während er durchläuft?**
A: Absolut! Das ist sogar Common Practice. Jeder Handler kann Request-Daten anreichern, validieren oder transformieren für die nachfolgenden Handler.
**F: Was ist mit asynchroner Verarbeitung?**
A: Moderne Implementierungen unterstützen oft async/await. Wichtig: Jeder Handler muss den nächsten Handler in der Chain korrekt awaiten.
**F: Wie unterscheidet sich das vom Strategy Pattern?**
A: Strategy wählt *einen* Algorithmus aus vielen. Chain of Responsibility probiert *mehrere* Handler nacheinander, bis einer den Request behandelt.
Die Schönheit von Chain of Responsibility liegt in ihrer Einfachheit. Sie löst nicht jedes Architektur-Problem – aber richtig angewendet verwandelt sie unwartbaren Spaghetti-Code in organisierte, lesbare Logik, für die dein zukünftiges Ich dir dankbar sein wird.
Technologie
Comments (0)
No comments yet. Be the first to comment!
Leave a Comment