Qual è lo scopo di una classe wrapper che non fa altro che delegare all'oggetto che avvolge?

2

L'esempio specifico che ho in mente è javax.servlet.ServletResponseWrapper :

public class ServletResponseWrapper implements ServletResponse {
    private ServletResponse response;

    /**
     * The default behavior of this method is to call setCharacterEncoding(String charset)
     * on the wrapped response object.
     *
     * @since 2.4
     */
    public void setCharacterEncoding(String charset) {
    this.response.setCharacterEncoding(charset);
    }

    /**
     * The default behavior of this method is to return getCharacterEncoding()
     * on the wrapped response object.
     */
    public String getCharacterEncoding() {
    return this.response.getCharacterEncoding();
    }

     /**
     * The default behavior of this method is to return getOutputStream()
     * on the wrapped response object.
     */

    public ServletOutputStream getOutputStream() throws IOException {
    return this.response.getOutputStream();
    }  

   /** and so on **/
}

ServletResponseWrapper fa niente . Passa solo l'esecuzione all'oggetto nidificato. Quindi ServletResponseWrapper non può funzionare a meno che non ci sia un'altra classe (diciamo ServletResponseConcrete ) che implementa anche ServletResponse e fa il vero lavoro.

In che contesto potresti utilizzare ServletResponseWrapper che non puoi semplicemente utilizzare ServletResponseConcrete direttamente? Questo non ha assolutamente senso per me. Non vedo come sia utile anche per la sottoclasse. Se sottoclassi ServletResponseWrapper, allora ho per implementare comunque ogni funzione, quindi perché non creare una classe che implementa ServletResponse direttamente?

    
posta Alexander Bird 15.12.2015 - 16:24
fonte

3 risposte

4

If I subclass ServletResponseWrapper, then I have to implement every function anyways, so why not just create a class that implements ServletResponse directly?

Questo non è vero. L'intera idea della classe è che si potrebbe usare come una classe base da cui ereditare e deve semplicemente sovrascrivere i metodi che devono essere modificati. Se si utilizza direttamente l'interfaccia, è necessario implementare tutti i metodi.

In what context would you be able to use ServletResponseWrapper that you couldn't just use ServletResponseConcrete directly?

Si potrebbe fornire un metodo che accetta un parametro di tipo ServletResponse o ServletResponseWrapper senza sapere che esiste un'implementazione chiamata ServletResponseConcrete . O forse ServletResponseConcrete non esiste nemmeno ancora. Ancora, potresti in seguito implementare ServletResponseConcrete o MyOtherServletResponse e passarlo a quel metodo.

Questo modello è chiamato "Delega". Ecco un semplice esempio di un'implementazione che aggiunge un System.out quando viene chiamato setLocale :

import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import java.util.Locale;

public class TimsServletResponse extends ServletResponseWrapper {

    public TimsServletResponse(ServletResponse response) {
        super(response);
    }

    @Override
    public void setLocale(Locale locale) {
        super.setLocale(locale);
        System.out.printf("setLocale was called (locale: %s)", locale);
    }
}

Crea un filtro per fare in modo che il server delle applicazioni utilizzi il wrapper in questo modo:

import javax.servlet.*;
import java.io.IOException;

public class TimsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) { }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        TimsServletResponse responseWrapper = new TimsServletResponse(response);
        chain.doFilter(request, responseWrapper);
    }

    @Override
    public void destroy() { }
}
    
risposta data 15.12.2015 - 16:29
fonte
1

L'esempio fornito nella documentazione è, in senso stretto, inutile per la progettazione . Se segui l'esempio dovresti aver implementato con successo una classe wrapper che non ha praticamente alcun effetto significativo sul sistema. Dovrebbe superare tutti i test appropriati, non commettere errori strani e non causare bizzarri bug.

Tuttavia, se segui la documentazione, impari a creare una classe wrapper appropriata e a nascondere i dettagli di implementazione privata che sono univoci per i tuoi sistemi. L'esempio mostra anche un'implementazione predefinita minima dell'interfaccia ServletResponse, che sarebbe utile se fosse necessaria una classe che implementasse questa interfaccia.

Quindi perché usare qualcosa che non fa nulla? Bene, c'è un passaggio 2 implicito: fagli fare qualcosa di utile che è specifico per i tuoi requisiti di sistema. Forse supportate solo determinati set di caratteri, o immettete sempre qualcosa in ServletResponse, oppure avete una speciale gestione write-behind quando viene chiamato reset() .

Per quanto riguarda il motivo per cui abbiamo sia un wrapper che un'implementazione concreta, il wrapper fondamentalmente fa il minimo richiesto per avvolgere l'oggetto sottostante, e il gioco è fatto. Quindi si utilizza il wrapper per codice per l'interfaccia , mentre l'implementazione concreta è piena dei dettagli di implementazione specifici del sistema che rendono lo sviluppo del mondo reale disordinato e impegnativo.

Il wrapper dovrebbe essere noioso - l'implementazione concreta è dove le cose si fanno interessanti. Tuttavia, se è necessario sostituire l'implementazione, ad esempio per supportare diverse piattaforme, architetture, ambienti o applicazioni particolari, tutto ciò che si fa è consegnare una classe Concrete diversa e il wrapper non si lamenterà o imporrà altri sistemi per sapere sulle tue lezioni a tutti Fintanto che implementano l'interfaccia giusta (che fa il tuo wrapper), saranno felici.

Questo è molto comune nelle impostazioni educative / documentali - l'esempio dato è l'applicazione minima del concetto, e di solito sembra assolutamente inutile. Ma una volta che conosci l'uso di qualcosa e dei principi, in seguito la documentazione serve come riferimento eccellente per implementare la cosa più semplice possibile che ti consenta di iniziare a lavorare sui bit più complessi.

    
risposta data 15.12.2015 - 16:41
fonte
0

L'utilità chiave di questa classe ha a che fare con come la usi.

Sì, come scritto la lezione è inutile. Ma non è pensato per essere usato direttamente - è solo qualcosa per passare attraverso i valori. Ma cosa succede se vuoi sovrascrivere il metodo one in cima alla risposta che hai in mano.

Bene, potresti creare un nuovo file .java che fa tutto ciò che vuoi.

In alternativa, puoi utilizzare una classe anonima .

Considera il codice:

public ServletResponse filter() {
    ServletResponse resp = something();
    /* I don't care, I  always want it to be 'UTF-8' */
    return new ServletResponseWrapper(resp) {
        @override
        public String getCharacterEncoding() {
          return "UTF-8";
        }
    };
}

Certo, si tratta di una cosa banale che si sarebbe potuta fare chiamando un setter nella risposta stessa, ma si ha l'idea di cosa potrebbe essere usato.

E qui c'è un bit di codice molto bello e conciso che utilizza ServletResponseWrapper. Sebbene il wrapper sia inutile così com'è, facilita l'altro codice che non è inutile. Il codice alternativo a questo è significativamente più dettagliato e le informazioni essenziali su ciò che fa potrebbero essere perse in quella verbosità.

    
risposta data 15.12.2015 - 17:22
fonte

Leggi altre domande sui tag