Chain of Responsibility 1 1 1
Chain of responsibility - definice skládá se z požadavků a z článků provádějících zpracování každý článek obsahuje logiku na zpracování určitých požadavků články navzájem propojeny do řetězu pokud článek neumí požadavek zpracovat, pošle ho dalšímu v řetězu řetěz obsahuje logiku pro přidávání dalších článků 2 2 2
Chain of responsibility - úč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 / změna za běhu příjemci tvoří spojový seznam předávají si požadavky (dokud je někdo nezpracuje) 3 3 3
Chain of responsibility – 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 4 4
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 50 Kč má pouze jeden vstup na mince… a několik zásobníků podle nominální hodnoty Kontextová nápověda obsah nápovědy dle závislosti na kontextu, ve kterém se 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 5 5 5
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; private set; } public float Diameter { get; private set; } public Coin( float weight, float diameter) { Weight = weight; Diameter = diameter; } abstract class CoinHandlerBase { protected CoinHandlerBase successor_; public abstract void HandleCoin( Coin coin); public void SetSuccessor( CoinHandlerBase successor){ successor_ = successor; } 6 6 6
rozdíl v podmínce a zpracování Chain of responsibility – implementace handlery pro konkrétní mince class OneCrownHandler : CoinHandlerBase { public override void HandleCoin( Coin coin) if( Abs( coin.Weight – 3.25) < 0.02) && Abs( coin.Diameter – 18) < 0.1) // Zpracování mince } else successor_.HandleCoin( coin); podmínka zpracování zřetězení class TwoCrownsHandler : CoinHandlerBase { … } class FiveCrownsHandler : CoinHandlerBase { … } class TenCrownsHandler : CoinHandlerBase { … } class TwentyCrownsHandler : CoinHandlerBase { … } class FiftyCrownsHandler : CoinHandlerBase { … } rozdíl v podmínce a zpracování 7 7 7
Chain of responsibility – implementace 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.47, weight = 6.5 ); Coin ten = new Coin( diameter = 27.24, weight = 8.1 ); Coin unknown = new Coin( diameter = 42.89, weight = 1.0 ); CoinHandlerBase handler = c1; handler.HandleCoin(one); handler.HandleCoin(ten); handler.HandleCoin(unknown); Vytvoření handlerů pro mince Nastavení pořadí zpracování Mince k použití Vstupní bod ?!? 8 8 8
Chain of responsibility – unknown řešení - default handler vytvoření handleru pro neznámé mince class UnknownHandler : CoinHandlerBase { public override void HandleCoin( Coin coin) // Zpracování neznáme mince } ... CoinHandlerBase c50 = new FiftyCrownsHandler(); CoinHandlerBase unknownHandler = new UnknownHandler(); c50.SetSuccessor(unknownHandler); Coin unknown = new Coin( diameter = 42.89, weight = 1.0 ); handler.HandleCoin(unknown); neznámá mince UnknownHandler 9 9 9
Chain of responsibility – logger jiné použití další handler je volán i po zpracování události public enum LogLevel { None = 0, Info = 1, Debug = 2, Warning = 4, Error = 8, All = 63 } public abstract class Logger { protected LogLevel logMask; protected Logger next; public Logger( LogLevel mask) { this.logMask = mask; } public Logger SetNext( Logger nextlogger) { next = nextlogger; return nextlogger; } public void Message( string msg, LogLevel severity) if( (severity & logMask) != 0) { // if logMask bits are set in severity WriteMessage( msg); } if( next != null) { next.Message(msg, severity); abstract protected void WriteMessage( string msg); chain next handler zřetězení vyvolání následníka 10 10 10
Chain of responsibility – logger handlery public class ConsoleLogger : Logger { public ConsoleLogger( LogLevel mask) : base(mask) {} protected override void WriteMessage( string msg) { WrtLn( "Console: " + msg); } } public class EmailLogger : Logger { public EmailLogger( LogLevel mask) : base(mask) {} protected override void WriteMessage( string msg) { WrtLn( "Email: " + msg); } class FileLogger : Logger { public FileLogger( LogLevel mask) : base(mask) {} protected override void WriteMessage( string msg) { WrtLn( "Log File: " + msg); } použití public class Program { public static void Main(string[] args) { Logger logger = new ConsoleLogger( LogLevel.All); Logger logger1 = logger.SetNext( new EmailLogger( LogLevel.Error)); Logger logger2 = logger1.SetNext( new FileLogger( LL.Warning | LL.Error)); logger.Message( "Entering function ProcessOrder()", LogLevel.Debug); logger.Message( "Address details missing", LogLevel.Warning); logger.Message( "Unable to Process Order ORD1 Dated D1", LogLevel.Error); } 11 11 11
Chain of responsibility – vzdálené zpracování Vzdálené zpracování požadavků skupina serverů poskytuje stejnou službu klientům záleží pouze na poskytnutí služby, ne na tom, kým je poskytnuta 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; } println( mID + " handled " + num ); return true; 12 12 12
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 println( mID + " handled " + num ); uzavření serverů „do kruhu“ každý požadavek musí být zpracován propojí poslední server s prvním nelze zpracovat, předá dál 13 13 13
nutná znalost interní struktury a počtu serverů Chain of responsibility – rozdíl v použití Bez použití NV S použitím NV void doit( request) { Server[] nodes = {new Server(), new Server(), new Server(), new Server()}; j = 0; while (!nodes[j].Handle(request)) j = (j + 1) % nodes.length; } void doit( request) { Server serverRoot = new Server(); serverRoot.Add( new Server()); serverRoot.WrapAround( serverRoot); serverRoot.Handle( request); } nutná znalost interní struktury a počtu serverů prostě to proveď 14 14 14
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) : Handler(h) {} virtual void Handle(); bool HasHandle(); }; void ConcreteHandler::HandleHelp () { if (HasHandle()) { // handle } else { Handler::HandleHelp(); } co když zapomenu zavolat funkci, která předává požadavek dál? 15
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) if(someCondition) { // do handling return true; return false; volání nasledníka lze vynutit prostředky jazyka naspoléhat se na programátora konkrétní handler pouze vrátí zda událost zpracoval 16
Chain of responsibility – 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 (klienta) nemusí se starat o strukturu příjemců neobsahuje reference na všechny možné příjemce 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 počítání mincí bez UnknownHandler implementace - default handler 17 17 17
Chain of responsibility – známé použití okenní systémy běžně používáno pro zpracování událostí jako kliknutí myši, stisk klávesy OSX, iOS Windows hooks požadavek projde všechny handlery Java Servlet Filter http request může zpracovat více filtrů síťové/distribuované systémy v řetězu zapojena množina serverů nabízejících služby klient předá požadavek libovolnému serveru servery si mezi sebou posílají požadavek dokud jej některý nezpracuje grafické toolkity (Java AWT – nevhodné použití, neujalo se) 18 18 18
Chain of Resposibility – souvisejíci NV Související NV Composite je-li řetěz objektů využívaný součástí rozsáhlejší (stromové) struktury, může být tato struktura tvořena pomocí Composite 19 19 19