Kihagyás

Chain of Responsibility (Felelősséglánc)

Kategória: Viselkedési minta (Behavioral Pattern)

Lényege és célja

A Chain of Responsibility minta egy kérést egy láncba kötött feldolgozók (handler) sorozatán enged végighaladni mindaddig, amíg valamelyikük fel nem dolgozza. A kérés feladójának nem kell tudnia, hogy a láncban ki fogja végül lekezelni az ügyet – csak az első elemnek adja át.

A kulcs ötlet: minden handler saját maga dönti el, hogy meg tudja-e oldani a problémát. Ha igen, lezárja a folyamatot; ha nem, továbbpasszolja a kérést a következőnek.

Miért jobb ez, mint egy nagy if-else?

Egy ügyfélszolgálati példán: súlyosság alapján Bot → Operátor → Menedzser sorrendben próbáljuk eszkalálni. Ha ezt egy nagy if-else-ben írnánk meg, a feladó össze lenne csatolva az összes szinttel. A láncban:

  1. Laza csatolás: a feladó csak a lánc első elemét ismeri, a többiről nem tud semmit.
  2. Bővíthetőség (Open/Closed): új szintet (pl. VipManagerHandler) úgy is be lehet szúrni, hogy a meglévő handler-ek kódjához nem kell hozzányúlni.
  3. Konfigurálhatóság: a lánc sorrendje és összetétele futásidőben szabadon átalakítható.

Mikor használjuk?

  • Amikor egy kérést több, eltérő szabályokkal rendelkező objektum közül egynek (vagy akár többnek) kell feldolgoznia, és előre nem tudjuk, melyiknek.
  • Amikor el akarunk kerülni egy hatalmas, központi if-else blokkot, ami a feldolgozó kiválasztásáról dönt (pl. eszkalációs szintek, validáló pipeline, middleware, log-szűrők).
  • Amikor a feldolgozási sorrend vagy a résztvevők listája dinamikusan változhat (pl. konfigurációból építjük össze).

Mermaid Diagram

A diagramon látható az absztrakt SupportHandler és a konkrét handler-ek kapcsolata. A nextHandler mező az önreferenciás kompozíció, ami a láncot összefűzi.

classDiagram
    class SupportHandler {
        <<abstract>>
        #nextHandler: SupportHandler
        +setNext(h: SupportHandler) SupportHandler
        #handleRequest(issue: String, severity: int) String
    }

    class BotHandler {
        #handleRequest(issue, severity) String
    }

    class OperatorHandler {
        #handleRequest(issue, severity) String
    }

    class ManagerHandler {
        #handleRequest(issue, severity) String
    }

    class Client

    BotHandler --|> SupportHandler : "örökli"
    OperatorHandler --|> SupportHandler : "örökli"
    ManagerHandler --|> SupportHandler : "örökli"

    %% Önmagára mutató kompozíció: a lánc következő eleme
    SupportHandler o--> SupportHandler : "next"

    Client --> SupportHandler : "csak az első elemet ismeri"

Forráskód

package behavioral.chain_of_responsibility;

import java.util.Objects;

abstract class SupportHandler {
    protected SupportHandler nextHandler;

    public final SupportHandler setNext(SupportHandler supportHandler) {
        if (nextHandler != null) {
            var lastInChain = nextHandler;
            while (lastInChain.nextHandler != null) {
                lastInChain = lastInChain.nextHandler;
            }
            lastInChain.nextHandler = supportHandler;
        } else {
            nextHandler = supportHandler;
        }

        return this;
    }

    protected String handleRequest(String issue, int severity) {
        if (Objects.nonNull(nextHandler)) {
            return nextHandler.handleRequest(issue, severity);
        } else {
            return "Sajnos a problémát nem tudtuk megoldani: [" + issue + "]";
        }
    }
}


class BotHandler extends SupportHandler {
    @Override
    protected String handleRequest(String issue, int severity) {
        if (severity <= 1) {
            return "A Bot automatikusan megoldotta a problémát: [" + issue + "]";
        } else {
            return super.handleRequest(issue, severity);
        }
    }
}

class OperatorHandler extends SupportHandler {
    @Override
    protected String handleRequest(String issue, int severity) {
        if (severity <= 2) {
            return "Az Operátor lekezelte az ügyet: [" + issue + "]";
        } else {
            return super.handleRequest(issue, severity);
        }
    }
}

class ManagerHandler extends SupportHandler {
    @Override
    protected String handleRequest(String issue, int severity) {
        if (severity <= 3) {
            return "A Menedzser egyedi elbírálással megoldotta: [" + issue + "]";
        } else {
            return super.handleRequest(issue, severity);
        }
    }
}

class Main {
    static void main() {
        /*
            Gof szerint mindig azzal kéne viszatérni amit kaptunk paraméterbe és kliensen kéne az elsőt példányosítani
            külön, majd ahhoz setnextelni új sorban a többiket, viszont ha így írom meg a setnextet így az elspőhöz
            lehet láncolni az összeset 1 kifejezésben, nekem így jobban tetszik.
         */
        var result = new BotHandler()
                .setNext(new OperatorHandler())
                .setNext(new ManagerHandler());

        System.out.println(result.handleRequest("Jelszó visszaállítás", 0));
        System.out.println(result.handleRequest("Jelszó visszaállítás", 2));
        System.out.println(result.handleRequest("Jelszó visszaállítás", 4));

    }
}