Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

Chain of responsibility

Návrh Návrhové vzory GOF Vzory chování Chain of responsibility

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

Chain of responsibility je návrhových vzor umožňující oddělení odesílatele požadavku od jeho příjemců, kterých může být více. Požadavek je předáván mezi příjemci až k tomu, který je kompetentní jej vyřídit. Implementace by měla počítat i se situací, kdy tento příjemce nebude nalezen.

Motivace

Pokud bychom napevno spojili třídu, která posílá nějaký požadavek, a třídu, která jej zpracovává, porušili bychom low coupling. Také bychom přišli o možnost napojení na více handlerů, které požadavek zpracovávají. Toho se v praxi často využívá při aplikování různých filtrů, které jsou v řetězu za sebou a požadavek postupně nějak zpracovávají.

Použití není omezené jen na lineární datové struktury, ale předávání odpovědnosti může probíhat i na úrovni k rodiči ve stromových strukturách. Někdy bývá vzor kombinovaný se vzorem Composite, který definuje nejefektivnější způsob jak takové stromové struktury vytvářet. Na takový strom někdy může být referováno jako na Tree of responsibility. Některé frameworky používají Chain of responsibility na implementaci událostního modelu.

Vzor

Asi vás nepřekvapí, že Handler, příjemce požadavku, je zde definován jako rozhraní. S tímto rozhraní komunikuje odesílatel požadavku a implementují jej jednotlivé handlery požadavku v řetězu nebo stromu. Ty jsou spolu propojené pomocí referencí.

Návrhový vzor Chain of responsibility z GOF

Handler typicky poskytuje metody pro nastavení dalšího handleru. Dále pro vyřízení požadavku, tato metoda je polymorfní. A nakonec pro předání požadavku dalšímu handleru. To může proběhnout i když byl požadavek zpracován částečně nebo vůbec, např. protože je handler zaneprázdněný nebo není pro zpracování daného požadavku kompetentní.

Příklad

Na chain of responsibility se dá vymyslet určitě plno příkladů, jelikož tímto způsobem funguje velké množství služeb. Když si např. objednáváte zboží z ciziny, váš požadavek vyřídí řetěz hned několika pošt. Reálné praktické užití ovšem zpravidla bývá v implementaci nějakých filtrů. Ty jsou pospojované za sebou a požadavek pohltí v případě, že není žádoucí. Až teprve poslední článek takovéhoto řetězce požadavek vyřídí.

Vytvořme si ukázku řetězu takových filtrů na požadavek na přijetí emailu, který nejprve poputuje spamfilterem, potom nějakým uživatelským filtrem, a teprve poté se potenciálně dostane k handleru, který jej vloží do příchozí pošty. Další handler může vyvolat upozornění na nový email. Udělejme si příklad přijetí emailu, který proputuje několika filtry. Ty bude možné libovolně přidávat a měnit právě díky vzoru.

Definujme abstrakci pro handlery v řetězu:

public abstract class HandlerPozadavku {

        private HandlerPozadavku dalsi;

        public HandlerPozadavku setDalsi(HandlerPozadavku dalsi) {
                this.dalsi = dalsi;
                return dalsi;
        }

        protected void nechVyriditDalsiho(Pozadavek pozadavek) {
                if (dalsi != null)
                        dalsi.vyridPozadavek(pozadavek);
        }

        public abstract void vyridPozadavek();

}

Všimněte si, že metoda setDalsi() vrací další handler v řetězu. To je proto, abychom mohli pomocí Method chaining rovnou poskládat celý řetěz. Např. takto:

spamFiltr.setDalsi(uzivatelskyFiltr).setDalsi(prichoziPostaHandler);

Řetěz můžeme jednoduše pospojovat na jediném řádku. Následuje možná podoba konkrétních handlerů:

public class SpamFiltr extends HandlerPozadavku {

        public void vyridPozadavek(Pozadavek pozadavek) {
                if (!pozadavek.Email.Text.Contains("free pills")) { // Jednoduchý spamfiltr
                        nechVyriditDalsiho(pozadavek);
                }
        }

}

public class UzivatelskyFiltr extends HandlerPozadavku {

        public List<String> zakazaneAdresy = new ArrayList<String>();

        public void vyridPozadavek(Pozadavek pozadavek)
        {
                if (!zakazaneAdresy.contains(pozadavek.email.adresa)) { // Jednoduchý uživatelský filtr
                        nechVyriditDalsiho(pozadavek);
                }
        }

}

public class PrichoziPostaHandler extends HandlerPozadavku {

        private PrichoziPosta posta;

        public PrichoziPostaHandler(PrichoziPosta posta) {
                this.posta = posta;
        }

        public void VyridPozadavek(Pozadavek pozadavek) {
                posta.pridej(pozadavek.email);
        }
}

Funkčnosti jednotlivých filtrů jsou samozřejmě extrémně zjednodušené a pouze ilustrativní. Celý řetěz bychom dali do pohybu předáním požadavku jeho prvnímu článku, kterým je SpamFiltr.

Související vzory

  • Composite - Chain of responsibility lze aplikovat kromě lineárních struktur i na struktury stromové, jejichž návrh řeší vzor Composite

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Předchozí článek
Iterator
Miniatura
Všechny články v sekci
GOF - Vzory chování
Aktivity (1)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!