Uzorak dizajna lanca odgovornosti na Javi

1. Uvod

U ovom ćemo članku pogledati široko korištene obrazac dizajna ponašanja: Lanac odgovornosti.

U našem prethodnom članku možemo pronaći više obrazaca dizajna.

2. Lanac odgovornosti

Wikipedia definira Lanac odgovornosti kao obrazac dizajna koji se sastoji od "izvora naredbenih objekata i niza obrađenih objekata".

Svaki objekt obrade u lancu odgovoran je za određenu vrstu naredbe i obrada je gotova, prosljeđuje naredbu sljedećem procesoru u lancu.

Lanac odgovornosti obrazac je zgodan za:

  • Odvajanje pošiljatelja i primatelja naredbe
  • Odabir strategije obrade u vrijeme obrade

Pa, pogledajmo jednostavan primjer uzorka.

3. Primjer

Koristit ćemo Lanac odgovornosti za stvaranje lanca za rukovanje zahtjevima za provjeru autentičnosti.

Dakle, pružatelj ulazne provjere autentičnosti bit će naredba, a svaki procesor za provjeru autentičnosti bit će zaseban procesor objekt.

Napravimo prvo apstraktnu osnovnu klasu za naše procesore:

javna apstraktna klasa AuthenticationProcessor {public AuthenticationProcessor nextProcessor; // standardni sažetak konstruktora boolean isAuthorized (AuthenticationProvider authProvider); }

Dalje, kreirajmo konkretne procesore koji se šire Procesor autentifikacije:

javna klasa OAuthProcessor proširuje AuthenticationProcessor {javni OAuthProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override javni logički isAuthorized (AuthenticationProvider authProvider) {if (authProvider instanceof OAuthTokenProvider) {return true; } else if (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } return false; }}
javna klasa UsernamePasswordProcessor proširuje AuthenticationProcessor {public UsernamePasswordProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override javni boolean isAuthorized (AuthenticationProvider authProvider) {if (authProvider instanceof UsernamePasswordProvider) {return true; } else if (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } return false; }}

Ovdje smo stvorili dva konkretna procesora za naše dolazne zahtjeve za autorizacijom: UsernamePasswordProcessor i OAuthProcessor.

Za svaku smo nadjačali isAuthorized metoda.

Izradimo sada nekoliko testova:

javna klasa ChainOfResponsibilityTest {privatni statički AuthenticationProcessor getChainOfAuthProcessor () {AuthenticationProcessor oAuthProcessor = novi OAuthProcessor (null); vratiti novi UsernamePasswordProcessor (oAuthProcessor); } @Test javna praznina givenOAuthProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertTrue (authProcessorChain.isAuthorized (novi OAuthTokenProvider ())); } @Test javna praznina givenSamlProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertFalse (authProcessorChain.isAuthorized (novi SamlTokenProvider ())); }}

Gornji primjer stvara lanac procesora za provjeru autentičnosti: UsernamePasswordProcessor -> OAuthProcessor. U prvom testu autorizacija uspije, a u drugom ne uspije.

Prvi, UsernamePasswordProcessor provjerava je li pružatelj autentifikacije instanca UsernamePasswordProvider.

Nije očekivani unos, UsernamePasswordProcessor delegati u OAuthProcessor.

Posljednji, OAuthProcessor obrađuje naredbu. U prvom se testu podudara i test prolazi. U drugom, u lancu više nema procesora i, kao rezultat, test ne uspijeva.

4. Principi provedbe

Moramo imati na umu nekoliko važnih principa tijekom provođenja lanca odgovornosti:

  • Svaki procesor u lancu imat će svoju implementaciju za obradu naredbe
    • U našem primjeru iznad, svi procesori imaju svoju implementaciju isAuthorized
  • Svaki procesor u lancu trebao bi imati referencu na sljedeći procesor
    • Iznad, UsernamePasswordProcessor delegati u OAuthProcessor
  • Svaki procesor odgovoran je za delegiranje sljedećem procesoru, pazite da ispustite naredbe
    • Opet u našem primjeru, ako je naredba instanca SamlProvider tada se zahtjev možda neće obraditi i bit će neovlašten
  • Procesori ne bi trebali stvarati rekurzivni ciklus
    • U našem primjeru nemamo ciklus u našem lancu: UsernamePasswordProcessor -> OAuthProcessor. Ali, ako izričito postavimo UsernamePasswordProcessor kao sljedeći procesor OAuthProcessor, tada završimo s ciklusom u našem lancu: UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. Uzimanje sljedećeg procesora u konstruktoru može vam u tome pomoći
  • Samo jedan procesor u lancu obrađuje zadanu naredbu
    • U našem primjeru, ako dolazna naredba sadrži instancu OAuthTokenProvider, tada samo OAuthProcessor će se nositi s naredbom

5. Korištenje u stvarnom svijetu

U svijetu Jave svakodnevno koristimo Lanac odgovornosti. Jedan od takvih klasičnih primjera su Servlet Filteri u Java koji omogućuju više filtara za obradu HTTP zahtjeva. Iako u tom slučaju, svaki filtar poziva lanac umjesto sljedećeg filtra.

Pogledajmo dolje isječak koda za bolje razumijevanje ovog uzorka u filtrima servleta:

javna klasa CustomFilter implementira Filter {public void doFilter (ServletRequest zahtjev, ServletResponse odgovor, FilterChain lanac) baca IOException, ServletException {// obrađuje zahtjev // prosljeđuje zahtjev (tj. naredbu) duž lanca lanca filtra.doFilter (zahtjev, odgovor ); }}

Kao što se vidi u isječku koda gore, moramo se pozvati FilterChain‘S doFilter metodu kako bi se zahtjev proslijedio sljedećem procesoru u lancu.

6. Mane

A sada kad smo vidjeli koliko je lanac odgovornosti zanimljiv, imajmo na umu neke nedostatke:

  • Uglavnom se lako može slomiti:
    • ako procesor ne uspije pozvati sljedeći procesor, naredba će pasti
    • ako procesor pozove pogrešni procesor, to može dovesti do ciklusa
  • Može stvoriti duboke tragove stoga, što može utjecati na performanse
  • To može dovesti do dupliciranja koda u procesorima, povećavajući održavanje

7. Zaključak

U ovom smo članku govorili o lancu odgovornosti i njegovim prednostima i slabostima uz pomoć lanca za autorizaciju dolaznih zahtjeva za provjeru autentičnosti.

Kao i uvijek, izvorni kod možete pronaći na GitHubu.