sobota, sierpnia 28

Chain of Responsibility


Purpose:

Gdy potrzebujemy by request mógł być obsłużony przez kilku odbiorców(handler). Odbiorcy są połączeniu ze sobą i przekazują wzajemnie między sobą wynik obsłużenia.


Use:

  • wiele obiektów może obsłużyć żądanie, nie jest zdeterminowane jaki obiekt będzie wstanie obsłużyć żądanie,
  • kilka obiektów może obsłużyć żądanie, obiekty obsługujące są determinowane w czasie wykonania (runtime),
  • jest do przyjęcia to, że żądanie nie zostanie obsłużone przez żaden obiekt.


Not Use:

  • każde żądanie obsługiwane jest tylko przez jedne obiekt,
  • gdy klient wie jaki serwis obsługuje jakie żądanie.


Example

Wzorzec ten realizowany jest przez obsługę wyjątków w niektórych językach. Gdy pojawia się wyjątek w metodzie, najpierw sprawdzane jest czy dana metoda posiada mechanizm obsługi, jeżeli dana metoda nie posiada mechanizmu obsługi wyjątek przekazywany jest do kolejnej metody na stosie. Przekazywanie kontynuowane jest do momentu napotkania metody potrafiącej obsłużyć wyjątek lub do skończenia się wywołań na stosie.


Resource:

5 komentarzy:

  1. Sporządziłem swego czasu dość kompleksowy przykład wzorca Chain of responsibility.

    Interesuje mnie przypadek, kiedy faktycznie wiemy, iż zawsze dokładnie jeden i tylko jeden Handler obsługuje dane żądanie. Czy wtedy odpowiedniejsza jest strategia? Tylko w jaki sposób wybrać konkretną implementację, jeśli jest ich np. kilkanaście? Gigantyczny if? W przypadku łańcucha każdy handler odpowiada sam na pytanie, czy jest w stanie obsłużyć żądanie czy nie - i deleguje dalej.

    OdpowiedzUsuń
  2. Witam,

    nie mogłem odpisać wcześnie, odpoczywałem morzem.

    Jeżeli chodzi o przypadek kiedy dokładnie wiemy, że jeden handler odpowiada za realizacje to użycie strategi będzie ok, tylko wtedy kiedy każdej naszej content do obsługi będzie odpowiadał jeden handler. Gdy zaczniemy przez jeden flow puszczać content obsługiwane przez strategię i chain zaczną się pojawiać if'y i pokusa dla kolejnych programistów, że można by tu jakiegoś if'a jeszcze wcisnąć.

    Moim zdaniem największą zaletą chain'a jest fakt, że ilość handlerów może się zmieniać dynamicznie i nasz kod powinien być na to odporny.

    Ja bym zbudował coś takiego,
    interface Content {
    public String content();
    public HandlerMark preferedHandler();
    }
    i jak to działa, nasz kontent musi rozszerzyć interface Content, HandlerMark to enum który zawiera wszystkie znaczniki handler'ów, chodzi o to, żeby w kodzie obsługi można było w łatwy sposób pobrać handler'a. Nie lubię null'i i exceptions dlatego też dodał bym jeszcze do HandlerMark pozycje NONE, oraz by połowa naszych klientów nie musiała implementować metody preferedHandler() w tej samej postaci dodał bym klasę abstrakcyjną
    abstrac class ContentWithNonePreferedHandler implements Content {
    public HandlerMark preferedHandler() {
    return HandlerMark.NONE;
    }
    }
    i teraz sama obsługa jest banalna, dodajemy na samy początku naszego chain'a jakiegoś Handlera w roli managera który to wykona taki kawałek kodu:

    void handle(Content content) {
    HandlerMark preferedHandler = content.preferedHandler();
    boolean isHandlerRegistered = handlers_.containsKey(conetnt.preferedHandler());
    if (HandlerMark.isCorrectHandlerMark(preferedHandler && isHandlerRegistered )) {
    handlers_.get(content.preferedHandler()).handle(content);
    }
    else {
    // do chain
    }
    }
    }

    oczywiście pozostaje jeszcze taka opcja, że nasz klient wybierze sobie Handler'a ale on nie będzie znajdował się w puli zarejestrowanych handler'ów (handlers_) i wtedy nasz content zostanie obsłużony przez chain;

    OdpowiedzUsuń
  3. Sorry, wkradł mi się mały błąd. Oczywiście linia:
    if (HandlerMark.isCorrectHandlerMark(preferedHandler && isHandlerRegistered )) {
    powinna wyglądać tak:
    if (HandlerMark.isCorrectHandlerMark(preferedHandler) && isHandlerRegistered) {

    OdpowiedzUsuń
  4. Mona podejsc tak, ze łańcuch zawiera strategie, a kazda strategia oprocz metody wyzwalającej jej wykonanie zawiera tez metode mowiącą czy dana strategia sie nadaje do pracy w danym przypadku:


    public void handleCase(ObjectToProcess problem){
    for (Strategy strategy : strategies){
    if (strategy.canHandle(problem)){
    strategy.handle(problem);
    break;
    }
    }
    }

    ew. mozna scalic canHandle() i handle() w jedną metodę, ktora zwroci boola - dzieki temu bedziemy wiedziec czy nalezy dalej szukac kandydata. oczywiscie wowczas nie da sie zwrocic wyniku ze strategii.

    rozwizanie ktore podalem wygląda imho czysto ale ma jedną wadę. strategie przestają byc reuzywalnymi i eleganckimi paczkami czynnosci poniewaz zostają zabrudzone metodą sprawdzajacą czy dana stragie się aplikuje. taka strategia jest juz niejako przyczepiona do łańcucha

    ale... jak to ze wzorcami - wszystko zalezy od konkretnego przypadku.

    generalnie we wzorcach najfajniejsze jest ich łączenie w wieksze struktury:)

    OdpowiedzUsuń
  5. 28 year-old Sales Associate Glennie Croote, hailing from Erin enjoys watching movies like It's a Wonderful Life and Web surfing. Took a trip to Birthplace of Jesus: Church of the Nativity and the Pilgrimage Route and drives a Jaguar E2A. Wypisz swoj adres url

    OdpowiedzUsuń