Chain of Responsibility (Ansvarskedja)
Introduktion
Chain of Responsibility är ett beteendemönster som låter dig skicka en begäran längs en kedja av hanterare. Varje hanterare avgör om den ska bearbeta begäran eller skicka den vidare till nästa i kedjan.
Problem
Du bygger ett system för att hantera supportärenden. Enkla frågor kan lösas av en bot. Tekniska problem behöver en supporttekniker. Faktureringsfrågor behöver ekonomiavdelningen. Chefsbeslut kräver en manager.
Med if/else i en enda metod växer logiken snabbt och varje ny ärendetyp kräver ändringar i kärnan av systemet.
Lösning
Chain of Responsibility låter varje hanterare antingen lösa begäran eller skicka den vidare till nästa länk i kedjan. Klientkoden skickar begäran till den första hanteraren — resten sker automatiskt.
När ska Chain of Responsibility användas?
- När mer än ett objekt kan hantera en begäran och hanteraren inte är känd i förväg.
- När du vill skicka en begäran till ett av flera objekt utan att explicit ange vilket.
- När en uppsättning hanterare ska kunna konfigureras dynamiskt.
Struktur
| Roll | Ansvar |
|---|---|
| Handler Interface | Deklarerar hanteringsmetoden och metod för att sätta nästa hanterare. |
| Base Handler | Valfri basklass med standardlogik för kedjehantering. |
| Concrete Handler | Hanterar begäran om möjligt, annars vidarebefordrar den. |
| Klient | Komponerar kedjan och skickar begäran till den första hanteraren. |
Exempel — Supportärendesystem (Java)
Scenariot: Ett supportärendesystem där ärenden eskaleras uppåt i kedjan baserat på prioritet.
// ── Ärendemodell ──────────────────────────────────────────
public enum Priority { LOW, MEDIUM, HIGH, CRITICAL }
public class SupportTicket {
private final String id;
private final String description;
private final Priority priority;
public SupportTicket(String id, String description, Priority priority) {
this.id = id;
this.description = description;
this.priority = priority;
}
public String getId() { return id; }
public String getDescription() { return description; }
public Priority getPriority() { return priority; }
}
// ── Handler Interface ─────────────────────────────────────
public interface SupportHandler {
SupportHandler setNext(SupportHandler next);
void handle(SupportTicket ticket);
}
// ── Base Handler ──────────────────────────────────────────
public abstract class BaseSupportHandler implements SupportHandler {
private SupportHandler next;
@Override
public SupportHandler setNext(SupportHandler next) {
this.next = next;
return next; // Möjliggör kedjning: a.setNext(b).setNext(c)
}
protected void passToNext(SupportTicket ticket) {
if (next != null) {
next.handle(ticket);
} else {
System.out.println("⚠️ Ärende " + ticket.getId() + " kunde inte hanteras i kedjan.");
}
}
}
// ── Concrete Handlers ─────────────────────────────────────
public class BotHandler extends BaseSupportHandler {
@Override
public void handle(SupportTicket ticket) {
if (ticket.getPriority() == Priority.LOW) {
System.out.println("🤖 Bot löste ärende " + ticket.getId() + ": " + ticket.getDescription());
} else {
System.out.println("🤖 Bot: Eskalerar ärende " + ticket.getId() + " (prioritet: " + ticket.getPriority() + ")");
passToNext(ticket);
}
}
}
public class TechSupportHandler extends BaseSupportHandler {
@Override
public void handle(SupportTicket ticket) {
if (ticket.getPriority() == Priority.MEDIUM) {
System.out.println("👨💻 Teknisk support löste ärende " + ticket.getId() + ": " + ticket.getDescription());
} else {
System.out.println("👨💻 Teknisk support: Eskalerar ärende " + ticket.getId());
passToNext(ticket);
}
}
}
public class ManagerHandler extends BaseSupportHandler {
@Override
public void handle(SupportTicket ticket) {
if (ticket.getPriority() == Priority.HIGH) {
System.out.println("👔 Manager löste ärende " + ticket.getId() + ": " + ticket.getDescription());
} else {
System.out.println("👔 Manager: Eskalerar ärende " + ticket.getId() + " till VD");
passToNext(ticket);
}
}
}
public class CeoHandler extends BaseSupportHandler {
@Override
public void handle(SupportTicket ticket) {
System.out.println("🏢 VD hanterar kritiskt ärende " + ticket.getId() + ": " + ticket.getDescription());
}
}
// ── Klientkod ─────────────────────────────────────────────
public class Application {
public static void main(String[] args) {
// Bygg kedjan
BotHandler bot = new BotHandler();
TechSupportHandler tech = new TechSupportHandler();
ManagerHandler manager = new ManagerHandler();
CeoHandler ceo = new CeoHandler();
bot.setNext(tech).setNext(manager).setNext(ceo);
// Skicka ärenden — kedjan avgör vem som hanterar
java.util.List<SupportTicket> tickets = java.util.List.of(
new SupportTicket("T-001", "Hur återställer jag mitt lösenord?", Priority.LOW),
new SupportTicket("T-002", "Appen kraschar vid inloggning", Priority.MEDIUM),
new SupportTicket("T-003", "Dataintrång misstänkt", Priority.HIGH),
new SupportTicket("T-004", "Hela systemet är nere för alla kunder", Priority.CRITICAL)
);
for (SupportTicket ticket : tickets) {
System.out.println("\n--- Nytt ärende: " + ticket.getId() + " ---");
bot.handle(ticket);
}
}
}
Output:
--- Nytt ärende: T-001 ---
🤖 Bot löste ärende T-001: Hur återställer jag mitt lösenord?
--- Nytt ärende: T-002 ---
🤖 Bot: Eskalerar ärende T-002 (prioritet: MEDIUM)
👨💻 Teknisk support löste ärende T-002: Appen kraschar vid inloggning
--- Nytt ärende: T-003 ---
🤖 Bot: Eskalerar ärende T-003 (prioritet: HIGH)
👨💻 Teknisk support: Eskalerar ärende T-003
👔 Manager löste ärende T-003: Dataintrång misstänkt
--- Nytt ärende: T-004 ---
🤖 Bot: Eskalerar ärende T-004 (prioritet: CRITICAL)
👨💻 Teknisk support: Eskalerar ärende T-004
👔 Manager: Eskalerar ärende T-004 till VD
🏢 VD hanterar kritiskt ärende T-004: Hela systemet är nere för alla kunder
Fördelar
- Single Responsibility Principle — Varje hanterare hanterar exakt ett ansvarsområde.
- Open/Closed Principle — Lägg till nya hanterare utan att ändra befintliga.
- Kedjan kan konfigureras och omordnas dynamiskt vid runtime.
Nackdelar
- Ingen garanti att begäran faktiskt hanteras — den kan nå slutet av kedjan utan åtgärd.
- Kan vara svår att debugga om kedjan är lång.
Av Victor Hernandez från Bytebase.se