Schema di progettazione della catena di responsabilità in Java

1. Introduzione

In questo articolo, daremo uno sguardo a un modello di progettazione comportamentale ampiamente utilizzato : Catena di responsabilità .

Possiamo trovare più modelli di design nel nostro articolo precedente.

2. Catena di responsabilità

Wikipedia definisce la Catena di responsabilità come un modello di progettazione costituito da "una fonte di oggetti di comando e una serie di oggetti di elaborazione".

Ogni oggetto di elaborazione nella catena è responsabile di un certo tipo di comando e l'elaborazione viene eseguita, inoltra il comando al processore successivo nella catena.

Il modello Catena di responsabilità è utile per:

  • Disaccoppiamento di mittente e destinatario di un comando
  • Scegliere una strategia di elaborazione al momento dell'elaborazione

Quindi, vediamo un semplice esempio del pattern.

3. Esempio

Utilizzeremo la catena di responsabilità per creare una catena per la gestione delle richieste di autenticazione.

Quindi, il provider di autenticazione dell'input sarà il comando e ogni processore di autenticazione sarà un oggetto processore separato .

Creiamo prima una classe base astratta per i nostri processori:

public abstract class AuthenticationProcessor { public AuthenticationProcessor nextProcessor; // standard constructors public abstract boolean isAuthorized(AuthenticationProvider authProvider); }

Successivamente, creiamo processori concreti che estendono AuthenticationProcessor :

public class OAuthProcessor extends AuthenticationProcessor { public OAuthProcessor(AuthenticationProcessor nextProcessor) { super(nextProcessor); } @Override public boolean isAuthorized(AuthenticationProvider authProvider) { if (authProvider instanceof OAuthTokenProvider) { return true; } else if (nextProcessor != null) { return nextProcessor.isAuthorized(authProvider); } return false; } }
public class UsernamePasswordProcessor extends AuthenticationProcessor { public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) { super(nextProcessor); } @Override public boolean isAuthorized(AuthenticationProvider authProvider) { if (authProvider instanceof UsernamePasswordProvider) { return true; } else if (nextProcessor != null) { return nextProcessor.isAuthorized(authProvider); } return false; } }

Qui, abbiamo creato due processori concreti per le nostre richieste di autorizzazione in entrata: UsernamePasswordProcessor e OAuthProcessor .

Per ognuno, abbiamo sovrascritto il metodo isAuthorized .

Ora creiamo un paio di test:

public class ChainOfResponsibilityTest { private static AuthenticationProcessor getChainOfAuthProcessor() { AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null); return new UsernamePasswordProcessor(oAuthProcessor); } @Test public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() { AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor(); assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider())); } @Test public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() { AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor(); assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider())); } }

L'esempio sopra crea una catena di processori di autenticazione: UsernamePasswordProcessor -> OAuthProcessor . Nel primo test l'autorizzazione ha esito positivo e nell'altro fallisce.

Innanzitutto, UsernamePasswordProcessor verifica se il provider di autenticazione è un'istanza di UsernamePasswordProvider .

Non essendo l'input previsto, UsernamePasswordProcessor delega a OAuthProcessor .

Infine, OAuthProcessor elabora il comando. Nel primo test, c'è una corrispondenza e il test passa. Nel secondo, non ci sono più processori nella catena e, di conseguenza, il test fallisce.

4. Principi di attuazione

Dobbiamo tenere a mente alcuni principi importanti durante l'implementazione della Catena di responsabilità:

  • Ogni processore nella catena avrà la sua implementazione per l'elaborazione di un comando
    • Nel nostro esempio sopra, tutti i processori hanno la loro implementazione di isAuthorized
  • Ogni processore nella catena dovrebbe fare riferimento al processore successivo
    • Sopra, UsernamePasswordProcessor delega a OAuthProcessor
  • Ogni processore è responsabile della delega al processore successivo, quindi fai attenzione ai comandi abbandonati
    • Sempre nel nostro esempio, se il comando è un'istanza di SamlProvider, la richiesta potrebbe non essere elaborata e non sarà autorizzata
  • I processori non dovrebbero formare un ciclo ricorsivo
    • Nel nostro esempio, non abbiamo un ciclo nella nostra catena: UsernamePasswordProcessor -> OAuthProcessor . Ma, se impostiamo esplicitamente UsernamePasswordProcessor come prossimo processore di OAuthProcessor, allora finiamo con un ciclo nella nostra catena : UsernamePasswordProcessor -> OAuthProcessor -> UsernamePasswordProcessor. Prendere il processore successivo nel costruttore può aiutare in questo
  • Solo un processore nella catena gestisce un dato comando
    • Nel nostro esempio, se un comando in arrivo contiene un'istanza di OAuthTokenProvider , solo OAuthProcessor gestirà il comando

5. Utilizzo nel mondo reale

Nel mondo Java, beneficiamo ogni giorno della catena di responsabilità. Uno di questi classici esempi sono i filtri servlet in Java che consentono a più filtri di elaborare una richiesta HTTP. Sebbene in tal caso, ogni filtro richiama la catena invece del filtro successivo.

Diamo un'occhiata allo snippet di codice di seguito per una migliore comprensione di questo modello nei filtri servlet :

public class CustomFilter implements Filter { public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // process the request // pass the request (i.e. the command) along the filter chain chain.doFilter(request, response); } }

Come visto nel frammento di codice di cui sopra, è necessario invocare FilterChain s' doFilter metodo per trasmettere la richiesta al processore successivo nella catena.

6. Svantaggi

E ora che abbiamo visto quanto sia interessante la catena di responsabilità, teniamo presente alcuni inconvenienti:

  • Per lo più, può rompersi facilmente:
    • se un processore non riesce a chiamare il processore successivo, il comando viene eliminato
    • se un processore chiama il processore sbagliato, può portare a un ciclo
  • Può creare tracce dello stack profonde, che possono influire sulle prestazioni
  • Può portare a duplicare il codice tra i processori, aumentando la manutenzione

7. Conclusione

In questo articolo, abbiamo parlato della catena di responsabilità e dei suoi punti di forza e di debolezza con l'aiuto di una catena per autorizzare le richieste di autenticazione in entrata.

E, come sempre, il codice sorgente può essere trovato su GitHub.