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.