Kihagyás

Template Method (Sablonmódszer)

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

Lényege és célja

A Template Method minta egy algoritmus vázát (a lépések sorrendjét) egy ősosztály final metódusába zárja, és csak azokat a lépéseket bízza az alosztályokra, amelyek tényleg változnak. Az algoritmus felépítése fix, csak a részletek cserélhetők.

A klasszikus példa az italkészítés: a folyamat mindig víz forralása → főzés → kitöltés → ízesítés. Ami változik: a főzés (kávé vs. teafű áztatása) és az ízesítés (cukor+tej vs. citrom+méz) – a BeverageMaker ősosztály ezt a két lépést hagyja absztraktan, a többit maga elvégzi.

Mit oldunk meg vele?

  1. Kódduplikáció megszüntetése: a közös lépéseket (víz forralása, csészébe töltés) egyszer írjuk meg, nem minden alosztályban.
  2. A folyamat sorrendjének védelme: mivel a sablon metódus final, az alosztály nem tudja átdrótozni a lépések sorrendjét – csak a tartalmukat változtatja.
  3. Hollywood-elv: "Don't call us, we'll call you." Nem az alosztály vezényli az ősosztályt, hanem az ősosztály hívja vissza ("hook") az alosztályt a megfelelő pillanatban. Ez ugyanaz a kontroll-megfordítás (IoC), amit a keretrendszerek (Spring, JUnit) is használnak.

Mikor használjuk?

  • Amikor több osztály ugyanazt a folyamatot hajtja végre, csak az egyes lépések különböznek (pl. különböző fájlformátumok parsolása: nyitás → fejléc → sorok → zárás).
  • Amikor keretrendszert írunk, és a felhasználónak csak bizonyos "hook" metódusokat akarunk engedni felülírni, a vázat zárva tartani.
  • Amikor el akarjuk kerülni, hogy ugyanaz a közös kód több alosztályban is megjelenjen (DRY).

Megjegyzés: A Template Method öröklésen keresztül variál, ezért szorosabb csatolást ad, mint a Strategy. Ha a "változó lépést" futásidőben is cserélgetni szeretnénk, gyakran a Strategy a jobb választás; ha a teljes folyamatváz fix és csak fordítási időben kell több verzió, a Template Method egyszerűbb.

Mermaid Diagram

A diagramon az absztrakt ősosztály (BeverageMaker) és a két konkrét készítő (CoffeeMaker, TeaMaker) kapcsolata látszik. A final makeBeverage() a sablon: rögzíti a lépések sorrendjét, és visszahívja az absztrakt brew() / addCondiments() metódusokat.

classDiagram
    class BeverageMaker {
        <<abstract>>
        +makeBeverage() void «final»
        #boilWater() void
        #pourInCup() void
        #brew()* void
        #addCondiments()* void
    }

    class CoffeeMaker {
        #brew() void
        #addCondiments() void
    }

    class TeaMaker {
        #brew() void
        #addCondiments() void
    }

    class Client

    CoffeeMaker --|> BeverageMaker : "örökli"
    TeaMaker --|> BeverageMaker : "örökli"

    Client --> BeverageMaker : "csak makeBeverage()-t hívja"

    note for BeverageMaker "makeBeverage() {\n  boilWater();\n  brew();        // alosztály\n  pourInCup();\n  addCondiments(); // alosztály\n}"

Forráskód

package behavioral.template_method;

abstract class BeverageMaker {

    final void makeBeverage() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    protected abstract void addCondiments();

    protected abstract void brew();

    private void pourInCup() {
        System.out.println("Kitöltés a bögrébe...");
    }

    private void boilWater() {
        System.out.println("Víz forralása...");
    }
}

class CoffeeMaker extends BeverageMaker {

    @Override
    protected void addCondiments() {
        System.out.println("Cukor és tej hozzáadása.");
    }

    @Override
    protected void brew() {
        System.out.println("Kávé lefőzése.");
    }
}

class TeaMaker extends BeverageMaker {

    @Override
    protected void addCondiments() {
        System.out.println("Citrom és méz hozzáadása.");
    }

    @Override
    protected void brew() {
        System.out.println("Teafű áztatása 3 percig.");
    }
}

class Main {
    static void main() {
        var coffeeMaker = new CoffeeMaker();
        coffeeMaker.makeBeverage();
        System.out.println();

        var teaMaker = new TeaMaker();
        teaMaker.makeBeverage();
    }
}