Chain of Responsibility Design Pattern i Java
1. Introduksjon
I denne artikkelen skal vi ta en titt på en mye brukt atferdsmønster: Ansvarskjede.
Vi kan finne flere designmønstre i vår forrige artikkel.
2. Ansvarskjede
Wikipedia definerer Chain of Responsibility som et designmønster som består av "en kilde til kommandoobjekter og en serie behandlingsobjekter".
Hvert behandlingsobjekt i kjeden er ansvarlig for en bestemt type kommando, og behandlingen er ferdig, den videresender kommandoen til neste prosessor i kjeden.
Mønsteret Chain of Responsibility er praktisk for:
- Koble fra en avsender og mottaker av en kommando
- Å velge en behandlingsstrategi på behandlingstidspunktet
Så la oss se et enkelt eksempel på mønsteret.
3. Eksempel
Vi skal bruke Chain of Responsibility til å lage en kjede for å håndtere autentiseringsforespørsler.
Så, leverandøren av inngangsgodkjenning vil være kommando, og hver godkjenningsprosessor vil være en egen prosessor gjenstand.
La oss først lage en abstrakt basisklasse for prosessorene våre:
offentlig abstrakt klasse AuthenticationProcessor {public AuthenticationProcessor nextProcessor; // standardkonstruktører offentlig abstrakt boolsk isAuthorized (AuthenticationProvider authProvider); }
La oss deretter lage konkrete prosessorer som strekker seg AuthenticationProcessor:
offentlig klasse OAuthProcessor utvider AuthenticationProcessor {public OAuthProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override public boolean isAuthorized (AuthenticationProvider authProvider) {if (authProvider instanceof OAuthTokenProvider) {return true; } annet hvis (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } returner falsk; }}
offentlig klasse UsernamePasswordProcessor utvider AuthenticationProcessor {public UsernamePasswordProcessor (AuthenticationProcessor nextProcessor) {super (nextProcessor); } @Override public boolean isAuthorized (AuthenticationProvider authProvider) {if (authProvider instanceof UsernamePasswordProvider) {return true; } annet hvis (nextProcessor! = null) {return nextProcessor.isAuthorized (authProvider); } returner falsk; }}
Her opprettet vi to konkrete prosessorer for våre innkommende autorisasjonsforespørsler: BrukernavnPassordProsessor og OAuthProsessor.
For hver enkelt overstyrer vi er autorisert metode.
La oss nå lage et par tester:
offentlig klasse ChainOfResponsibilityTest {privat statisk AuthenticationProcessor getChainOfAuthProcessor () {AuthenticationProcessor oAuthProcessor = ny OAuthProcessor (null); returner nytt brukernavnPassordProsessor (oAuthProcessor); } @Test offentlig ugyldig gittOAuthProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertTrue (authProcessorChain.isAuthorized (ny OAuthTokenProvider ())); } @Test offentlig ugyldighet givenSamlProvider_whenCheckingAuthorized_thenSuccess () {AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor (); assertFalse (authProcessorChain.isAuthorized (ny SamlTokenProvider ())); }}
Eksemplet ovenfor skaper en kjede av autentiseringsprosessorer: BrukernavnPassordProsessor -> OAuthProsessor. I den første testen lykkes autorisasjonen, og i den andre mislykkes den.
Først, BrukernavnPassordProsessor kontrollerer om godkjenningsleverandøren er en forekomst av BrukernavnPassordTilbyder.
Ikke det forventede innspillet, BrukernavnPassordProsessor delegater til OAuthProsessor.
Sist, den OAuthProsessor behandler kommandoen. I den første testen er det kamp og testen består. I det andre er det ikke flere prosessorer i kjeden, og som et resultat mislykkes testen.
4. Implementeringsprinsipper
Vi må ha noen få viktige prinsipper i bakhodet mens vi implementerer kjeden av ansvar:
- Hver prosessor i kjeden vil ha sin implementering for å behandle en kommando
- I vårt eksempel ovenfor har alle prosessorer implementeringen av dem er autorisert
- Hver prosessor i kjeden skal ha referanse til neste prosessor
- Ovenfor, BrukernavnPassordProsessor delegater til OAuthProsessor
- Hver prosessor er ansvarlig for å delegere til neste prosessor, så pass på kommandoer som er droppet
- Igjen i vårt eksempel, hvis kommandoen er en forekomst av SamlProvider da blir forespørselen kanskje ikke behandlet og vil være uautorisert
- Prosessorer skal ikke danne en rekursiv syklus
- I vårt eksempel har vi ikke en syklus i kjeden vår: BrukernavnPassordProsessor -> OAuthProsessor. Men hvis vi eksplisitt setter det BrukernavnPassordProsessor som neste prosessor av OAuthProsessor, så ender vi opp med en syklus i kjeden vår: BrukernavnPassordProsessor -> OAuthProsessor -> BrukernavnPassordProsessor. Å ta neste prosessor i konstruktøren kan hjelpe med dette
- Bare en prosessor i kjeden håndterer en gitt kommando
- I vårt eksempel, hvis en innkommende kommando inneholder en forekomst av OAuthTokenProvider, bare da OAuthProsessor vil håndtere kommandoen
5. Bruk i den virkelige verden
I Java-verden drar vi nytte av Chain of Responsibility hver dag. Et slikt klassisk eksempel er Servlet Filters in Java som tillater flere filtre å behandle en HTTP-forespørsel. Skjønt i så fall hvert filter påkaller kjeden i stedet for neste filter.
La oss ta en titt på kodebiten nedenfor for bedre forståelse av dette mønsteret i Servlet-filtre:
offentlig klasse CustomFilter implementerer Filter {public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) kaster IOException, ServletException {// behandler forespørselen // send forespørselen (dvs. kommandoen) langs filterkjedekjeden. doFilter (forespørsel, svar ); }}
Som vi ser i kodebiten ovenfor, må vi påberope oss Filterkjede‘S doFilter metode for å overføre forespørselen til neste prosessor i kjeden.
6. Ulemper
Og nå som vi har sett hvor interessant Chain of Responsibility er, la oss huske på noen ulemper:
- For det meste kan det lett ødelegges:
- hvis en prosessor ikke ringer til neste prosessor, blir kommandoen sluppet
- hvis en prosessor kaller feil prosessor, kan det føre til en syklus
- Det kan skape dype stakkspor, som kan påvirke ytelsen
- Det kan føre til duplikatkode på tvers av prosessorer, noe som øker vedlikeholdet
7. Konklusjon
I denne artikkelen snakket vi om Chain of Responsibility og dets styrker og svakheter ved hjelp av en kjede for å autorisere innkommende autentiseringsforespørsler.
Og som alltid kan kildekoden bli funnet på GitHub.