Felelősséglánc programtervezési minta

Az objektumorientált tervezésben a felelősséglánc egy tervezési minta, amely parancsobjektumokból és feldolgozó objektumokból áll. Minden egyes feldolgozó objektum tartalmazza azt a logikát, amellyel a parancsobjektum definiálható és kezelhető, valamint olyan folyamatokat, amiket kidolgozásra továbbadhat a lánc egy következő folyamatának. Olyan mechanizmus is létezik, amivel lehetőségünk adódik arra, hogy a felelősséglánc végén egy újabb feldolgozó objektumot adjunk hozzá.

A standard felelősséglánc egyik variációja úgy viselkedik, mint egy diszpécserközpont, mert képes parancsokat küldeni különböző irányokba, amivel át tudja alakítani a felelősségláncot. Bizonyos esetekben előfordulhat rekurzió. Ez akkor történik, amikor az éppen aktuálisan futó objektum meghív egy magasabban futó műveletet egy paranccsal, aminek az a célja, hogy a futó objektum kisebb részekre vágásával megpróbálja megoldani a problémát. Ebben az esetben a rekurzió vagy addig folytatódik, amíg a parancs futását le nem állítjuk, vagy addig, amíg az összes ágat be nem futja. Az XML fordító működik rekurzív módon.

Ez a tervezési minta hozzájárult a laza csatoltság ötletéhez, ami az egyik legjobb programozási gyakorlatnak tekinthető.

Példa szerkesztés

Java példa szerkesztés

Az alábbi példa bemutatja a tervezési mintát Java nyelven. Ebben a példában különböző szerepeket mutatunk be, ezek mindegyike tartalmaz egy fix fizetési limitet, valamint öröklődést. Minden alkalommal, amikor a felhasználó meghívja a fizetési kérést, és a kérésben szereplő összeg meghaladja a fixre állított fizetési limitet, a kérést átadja az osztály a gyermekének.

A PhurchasePower egy abstract osztály, egy processRequest nevű absztrakt metódussal.

abstract class PurchasePower {
    protected static final double BASE = 500;
    protected PurchasePower successor;

    public void setSuccessor(PurchasePower successor) {
        this.successor = successor;
    }

    abstract public void processRequest(PurchaseRequest request);
}

Az abstract osztály felett négy megvalósítás van: Manager, Director, Vice President, President.

class ManagerPPower extends PurchasePower {
    private final double ALLOWABLE = 10 * BASE;

    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Manager will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}

class DirectorPPower extends PurchasePower {
    private final double ALLOWABLE = 20 * BASE;

    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Director will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}

class VicePresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 40 * BASE;

    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("Vice President will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}

class PresidentPPower extends PurchasePower {
    private final double ALLOWABLE = 60 * BASE;

    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < ALLOWABLE) {
            System.out.println("President will approve $" + request.getAmount());
        } else {
            System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!");
        }
    }
}

A következő kód definiálja a PurchaseRequest osztályt és beállítja a kért adatokat.

class PurchaseRequest {
    private double amount;
    private String purpose;

    public PurchaseRequest(double amount, String purpose) {
        this.amount = amount;
        this.purpose = purpose;
    }

    public double getAmount() {
        return amount;
    }
    public void setAmount(double amt) {
        amount = amt;
    }

    public String getPurpose() {
        return purpose;
    }
    public void setPurpose(String reason) {
        purpose = reason;
    }
}

A következő példában az örökös osztályok beállítják a korábban definiált megvalósításokat, ebben a sorrendben: Manager -> Director -> Vice President -> President.

class CheckAuthority {
    public static void main(String[] args) {
        ManagerPPower manager = new ManagerPPower();
        DirectorPPower director = new DirectorPPower();
        VicePresidentPPower vp = new VicePresidentPPower();
        PresidentPPower president = new PresidentPPower();
        manager.setSuccessor(director);
        director.setSuccessor(vp);
        vp.setSuccessor(president);

        // Press Ctrl+C to end.
        try {
            while (true) {
                System.out.println("Enter the amount to check who should approve your expenditure.");
                System.out.print(">");
                double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine());
                manager.processRequest(new PurchaseRequest(d, "General"));
           }
        } catch(Exception e) {
            System.exit(1);
        }
    }
}

Megvalósítások szerkesztés

A Cocoa és a Cocoa Touch keretrendszer szerkesztés

A Cocoa és Cocoa Touch keretrendszert az OS X és iOS operációs rendszerek használják. A két keretrendszer gyakran használja a felelősséglánc tervezési mintát az események kezelésére.Az objektumok meghívnak egy válaszadó objektumot, ami öröklődik az NSResponder (OS X)/UIResponder (iOS) osztályokban. Az összes nézet objektumban (NSView/UIView), nézetet irányító objektumban (NSViewController/UIViewController), ablak objektumokban (NSWindow/UIWindow), valamint az applikációs objektumban (NSApplication/UIApplication) van válaszadó objektum

Tipikusan, amikor a nézet kap egy eseményt, amit nem tud kezelni, kiszervezi azt a nézet irányító, vagy az ablak objektumokba. Ha ezek az objektumok sem tudják kezelni az eseményt, akkor ők is kiszervezik az applikációs objektum felé, ami a láncban az utolsó objektum.

Például:

  • OS X-en, amikor megmozdítasz egy ablakot az egérrel, akkor azt normál estben bárhová le lehet tenni. Kivételt képez az a helyzet, amikor egy másik esemény útban van (pl. csúszkairányító), Ha nincs nézet (vagy szupernézet), hogy azt lekezelje, akkor az operációs rendszer elküldi az eseményt a felelősségláncba, átadva így a felelősséget annak.
  • iOS-en éppen a mozgatott esemény az, amelyik vezeti a hierarchiát a nézet alatti osztály helyett. Övé a prioritás, és ha ez problémát okoz, akkor a válaszadó lánc felfüggeszti az összes nézeteseményt, és lekezelni a problémás részt.

Kapcsolódó szócikkek szerkesztés

További információk szerkesztés

Fordítás szerkesztés

Ez a szócikk részben vagy egészben a Chain-of-responsibility pattern című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.