Chain of Responsibility
Chain of responsibility - účel Účel Umožňuje zrušení vazby mezi odesílatelem a příjemcem požadavku Umožňuje zaslání požadavku (zprávy) neznámým příjemcům Nemusí být předem zřejmé, který objekt požadavek obslouží Snadná rozšiřitelnost, či změna za běhu Příjemci tvoří spojový seznam předávají si požadavky (dokud je někdo nezpracuje)
Chain of responsibility – struktura Struktura Účastníci Handler definuje rozhraní pro zpracování požadavků volitelně implementuje ukazatel na následníka ConcreteHandler zachytává požadavek, který umí zpracovat může přistupovat na svého následníka Client vyvolává požadavek předáním na objekt ConcreteHandler zapojený do řetězu
Chain of responsibility - příklady Subsystém pro počítání hodnoty mincí Nápojový automat přijímá mince o nominálních hodnotách 1, 2, 5, 10, 20 a 50Kč. Má pouze jeden vstup na mince… a několik zásobníků na mince (podle nominální hodnoty). Kontextová nápověda widgetu (viz GoF) Obsah nápovědy dle závislosti na kontextu, ve kterém se widget nachází. Vzdálené zpracování požadavků Skupina serverů poskytuje nějakou (stejnou) službu. Klientům záleží pouze na poskytnutí služby, ne na tom, kým je nakonec poskytnuta.
Chain of responsibility – implementace Subsystém pro počítání hodnoty mincí Nominální hodnota mince je určena rozměry a hmotností Bázový handler class Coin { public float Weight { get; set; } public float Diameter { get; set; } } abstract class CoinHandlerBase { protected CoinHandlerBase _successor; public abstract void HandleCoin(Coin coin); public void SetSuccessor(CoinHandlerBase successor) { _successor = successor; } Lze samozřejmě předávat například v konstruktoru
Chain of responsibility – implementace Handlery pro konkrétní mince class OneCrownHandler : CoinHandlerBase { public override void HandleCoin(Coin coin) { if (Math.Abs(coin.Weight – 3,25) < 0,02) && Math.Abs(coin.Diameter – 18) < 0,1) { // Zpracování mince } else { _successor.HandleCoin(coin); } Je tento handler určen ke zpracování dodané mince? class TwoCrownsHandler : CoinHandlerBase { … } class FiveCrownsHandler : CoinHandlerBase { … } class TenCrownsHandler : CoinHandlerBase { … } class TwentyCrownsHandler : CoinHandlerBase { … } class FiftyCrownsHandler : CoinHandlerBase { … } Rozdíl pouze v klauzuli if funkce HandleCoin(), jinak totožné s příkladem OneCrown
Chain of responsibility – implementace Použití CoinHandlerBase c1 = new OneCrownHandler(); CoinHandlerBase c2 = new TwoCrownsHandler(); CoinHandlerBase c5 = new FiveCrownsHandler(); CoinHandlerBase c10 = new TenCrownsHandler(); CoinHandlerBase c20 = new TwentyCrownsHandler(); CoinHandlerBase c50 = new FiftyCrownsHandler(); c1.SetSuccessor(c2); c2.SetSuccessor(c5); c5.SetSuccessor(c10); c10.SetSuccessor(c20); c20.SetSuccessor(c50); Coin one = new Coin( Diameter = 24,47F, Weight = 6,5F ); Coin ten = new Coin( Diameter = 27,24F, Weight = 8,1F ); Coin unknown = new Coin( Diameter = 42,89F, Weight = 1,0F ); CoinHandlerBase handler = c1; handler.HandleCoin(one); handler.HandleCoin(ten); handler.HandleCoin(unknown); Vytvoření handlerů pro validní mince ?!? Mince k použití Vstupní bod do subsystému Nastavení pořadí zpracování
Chain of responsibility – unknown Řešení class UnknownHandler : CoinHandlerBase { public override void HandleCoin(Coin coin) { // Zpracování neznáme mince } Vytvoření handleru pro neznámé mince... CoinHandlerBase c50 = new FiftyCrownsHandler(); CoinHandlerBase unknownHandler = new UnknownHandler();... c50.SetSuccessor(unknownHandler);... Coin unknown = new Coin( Diameter = 42,89F, Weight = 1,0F );... handler.HandleCoin(unknown); Neznámé mince zpracuje UnknownHandler
Chain of responsibility – vzdálené zpracování Vzdálené zpracování požadavků Skupina serverů poskytuje nějakou (stejnou) službu. Klientům záleží pouze na poskytnutí služby, ne na tom, kým je nakonec poskytnuta. Vzdálené zpracování požadavků – bez použití NV Definice serveru a jeho služeb – původní řešení class Server { private static int mNextID = 1; private int mID = mNextID++; private bool IsBusy() {…} public bool handle(int num) { if (IsBusy()) { return false; } System.out.println( mID + " handled " + num ); return true; }
Chain of responsibility – vzdálené zpracování Vzdálené zpracování požadavků – s použitím NV Definice serveru a jeho služeb – nové řešení class Server { private static int mNextID = 1; private int mID = mNextID++; private Server mNextServer; private bool IsBusy() {…} public void add(Server next) { if (mNextServer == null) mNextServer = next; else mNextServer.add(next); } public void wrapAround(Server root) { if (mNextServer == null) mNextServer = root; else mNextServer.wrapAround(root); } public void handle(int num) { if (IsBusy()) mNextServer.handle(num); else System.out.println( mID + " handled " + num ); } Server nemůže zpracovat požadavek, předává ho dále Funkce slouží k uzavření serverů „do kruhu“, každý požadavek musí být zpracován Propojí poslední server s prvním
Chain of responsibility – rozdíl v použití Bez použití NV public class ChainDemo { public static void main(String[] args) { Server[] nodes = {new Server(), new Server(), new Server()}; int request = 1; j = 0; while (!nodes[j].handle(request)) j = (j + 1) % nodes.length; } S použitím NV public class ChainDemo { public static void main(String[] args) { Server serverRoot = new Server(); serverRoot.add( new Server() ); serverRoot.wrapAround( serverRoot ); int request = 1; serverRoot.handle(request); }
Chain of responsibility - následník Class Handler { public: HelpHandler(HelpHandler* s) : _successor(s) { } virtual void Handle (); private: Handler* _successor; }; void Handler::Handle() { if (_successor) { _successor->HandleHelp(); } class ConcreteHandler : public Handler { public: ConcreteHandler(Handler* h); virtual void Handle(); bool HasHandle(); }; ConcreteHandler:: ConcreteHandler(Handler* h) : Handler(h) { } void ConcreteHandler::HandleHelp () { if (HasHandle()) { // handle } else { HelpHandler::HandleHelp(); } Co když zapomenu zavolat nejakou funkci, která předává požadavek dál?
Chain of responsibility - následník public abstract class ClassicChain { private ClassicChain next; public ClassicChain(ClassicChain nextNode) { next = nextNode; } public final void start(ARequest request) { boolean handledByThisNode = this.handle(request); if (next != null && !handledByThisNode) next.start(request); } protected abstract boolean handle(ARequest request); } public class AClassicChain extends ClassicChain { protected boolean handle(ARequest request) { boolean handledByThisNode = false; if(someCondition) { //Do handling handledByThisNode = true; } return handledByThisNode; } Volání nasledníka vynutíme pomocí programovacího jazyka, nikoliv pomocí paměti programátora
Chain of responsibility – s podtřídami Zpracování požadavků s podtřídami: Podtřída zpracuje pouze požadavky, o níž má zájem, další požadavky jsou předávány do rodičovské tříd y Třeba definovat typ požadavku class ExtendedHandler : public Handler { public: virtual void HandleRequest(Request* theRequest); //... }; void ExtendedHandler::HandleRequest (Request* theRequest) { switch (theRequest->GetKind()) { case Preview: // handle the Preview request break; default: // let Handler handle other requests Handler::HandleRequest(theRequest); }
Chain of responsibility – použitelnost Použitelnost Chceme zaslat zprávu a nevíme kdo ji zpracuje nebo nás to nezajímá Důležitý je výsledek! Pokud více než jeden příjemce může přijmout zprávu a není apriori známo který Výhody Odděluje odesílatele od příjemců Zjednodušuje odesílatele – neobsahuje reference na všechny možné příjemce – viz „vzdálené zpracování požadavků“ Možnost měnit řetěz dynamicky, za běhu programu Nevýhody není zaručeno, že nějaký příjemce zprávu přijme – viz „subsystém pro počítání hodnoty mincí bez UnknownHandler“ při implementaci na to tedy musíme myslet
Chain of responsibility – známé použití Známé použití Grafické toolkity (Java AWT – nevhodné použití, neujalo se) Okenní systémy běžně používáno pro zpracování událostí jako kliknutí myši, stisk klávesy Windows hooks Požadavek projde všechny handlery Java Servlet Filter Http request může zpracovat více filtrů Distribuované systémy v řetězu (do kruhu) je zapojena množina serverů nabízejících určité služby klient předá požadavek libovolnému serveru servery si mezi sebou posílají požadavek, dokud jej některý nezpracuje
Chain of Resposibility – souvisejíci NV Související NV Composite Je-li řetěz objektů využívaný součástí rozsáhlejší struktury, je tato struktura obvykle tvořena pomocí Composite vzoru
Chain of Resposibility – zdroje GoF Různé detaily implementace Nepoužité příklady Responsibility.htm Použití ve Windows hooks