Struts lo dici a tua sorella!

Non che io abbia niente contro Struts.. niente di serio quanto meno. Però mi da in testa quando mi si dice che un certo framework “ti permette di”. Al massimo un framework “ti offre”, nel senso che se non lo usi puoi comunque ottenere quello che vuoi.
Devo ancora vedere un framework che faccia qualcosa che non posso ottenere altrimenti. Alcuni dei framework più comuni che ho usato mi hanno dimostrato il loro valore quali strumenti di incremento della produttività, ma sono sempre più convinto che nella maggior parte dei casi un framework sia visto come necessario solo perchè si è abituati a team di sviluppo che lavorano su linguaggi OO, ma che non si preoccupano minimamente del design OO.
In quel caso un framework ha una sua utilità, in quanto impone una certa architettura e, fino a un certo punto, guida la distribuzione del codice, che altrimenti sarebbe del tutto aleatoria. In effetti io ho una mia piccola teoria evoluzionistica sui perchè e i percome della salita al potere dei framework, ma non voglio perdermici qui.

Sta di fatto che certe cose sembrano proprio darmi ragione :
il mese scorso mi trovavo a discutere con un collega un’eventuale collaborazione per un progettino interno di cui aveva iniziato a occuparsi.
Non so perchè, forse quel giorno si sentiva illuminato o forse pensava di parlare con un altro, ma ha iniziato a introdurmi quello che aveva fatto come se non sapessi nulla di J2EE, così, dall’inizio della discussione. Dopo avergli assicurato in un paio di occasioni, che sì, sapevo cos’era un application server, e non avendo ottenuto null’altro che dei sorrisi accondiscendenti, ho deciso che probabilmente era stato colpito da Pierangelite acuta e sentiva quindi un bisogno forsennato di divulgare le basi della tecnica, e ho smesso di rassicurarlo, lasciandolo alla sua lezione.

“Noi, qui, usiamo Struts” dichiara a un certo punto il mio mentore (notare che siamo nello stesso ufficio da mesi, non avevamo mai lavorato assieme direttamente, ma diciamo pure che sviluppiamo sullo stesso sistema: il più classico degli Struts) e io azzardo un “Eh, già.”

“Conosci MVC?” e io : “Beh, sì, se intendi il pattern Model View Controller”.
“Intendo Struts..” e io : “Sì, che sfrutta MVC per l’appunto.. daltronde MVC è un po’ come il prezzemolo non appena si ha a che fare con interfaccie utente di qualsiasi tipo.”

Insomma, ha proseguito spiegandomi cos’era la vista e cos’era il controller (sul modello ha un po’ sorvolato, forse perchè si avvicinava la ricreazione).

La “lezione” mi ha fatto molto riflettere.

E’ un po’ come se il Model View Controller fosse questo dono di dio che ci viene offerto da Struts e che altrimenti noi non potremmo mai raggiungere.

Ora, diciamocelo : MVC è banale.. non c’è bisogno di usare Java + JSP + framework per ottenere una web application che rispetti le direttive MVC.

Visto che sono stato malato in questi giorni ho rifattorizzato una servlettina che stiamo sviluppando e che, per quanto chiara, accoppiava strettamente la business logic alla presentation. Ora, questo è male, soprattutto se il tutto è spruzzato di cablatura per passare da una vista all’altra.

In particolare la business logic era pochina, mentre l’interfaccia utente era la parte maggiore della componente e si stava insinuando un po’ troppo nel pur piccolo dominio dell’applicazione.

Per trovare i problemi di design io tendo a partire con il Single Responsibility Principle, che è un po’ il mio cavallo di battaglia. Ho guardato le varie classi che avevo usato per montare la pagina HTML, poi ho guardato i doPost e i doGet.

Alcuni dei nodi HTML che io producevo con le mie classi avevano di fatto 2 o 3 responsabilità, prendiamo ad esempio la classe UploadForm che sputa fuori questo HTML :

<form method='POST' enctype='multipart/form-data' action='/MyServlet'><input type=file name=upfile><br><input type=text name=name><br><br><input type=submit value=Press>Upload!</form>

Questa non è tutta presentazione. Lo dimostra il fatto che io nella mia doPost cerco il parametro “name” e che se ci fosse più di una post che chiama la mia servlet dovrei usare due servlets diverse o passarmi un parametro discriminante tra i due form, magari potrei aggiungere :
<input type=hidden name=formID value=1>

Quindi l’elenco degli inputs e l’indirizzo a cui mira la action sono elementi la cui definizione non riguarda questioni di presentazione, quanto questioni di controllo. Se il mio controllo decide di discriminare con handlerID io devo andare a pescare tutte le classi che generano i form e modificarle. Se sposto la mia servlet, o se la rinomino, devo modificare la action.

Questa classe non è chiusa in alcun modo.

Stesso vale per la mia doPost, che rischia di diventare un metodone pieno di “if” con la responsabilità sia di discriminare che di agire.

Prendo quindi l’azione compiuta dalla post e i controlli che dicono se e quando farla e la metto dentro a una classe UploadHandler, ci estraggo una interfaccia PostHandler che dice più o meno così:

public interface PostHandler {
boolean isInvolved(HttpServletRequest request);
void handle();
}

Faccio lo stesso per la doGet, e mi ritrovo con l’interfaccia GetHandler, identica a PostHandler.

A questo punto devo togliere una responsabilità a quelle classi che generano un HTML legato ai controlli che poi effettuano gli handlers. Per ora il posto migliore per questa responsabilità è l’handler stesso, visto che è di fatto un altro aspetto della stessa responsabilità già posseduta dagli handler : la discriminazione in base ai parametri (voglio richiamare sempre la stessa servlet, quindi non discrimino sulla action).

Aggiungo così il metodo “getInputs(String… parameters)” a PostHandler e quelle classi che devono definire un form si devono ora registrare presso un PostHandler che poi useranno per definire gli input discriminanti della loro post.
Una cosa così :

public class UploadForm extends HtmlElement implements Actor {

private PostHandler handler;

void register(PostHandler handler) {
this.handler = handler;
}

public void print(OutputStream out){
out.println(“<form method=’POST’ enctype=’multipart/form-data’ action=’/MyServlet’>”);
out.println(“<input type=file name=upfile><br>”);
out.println(“<input type=text name=name><br>”);
out.println(handler.getInputs());
out.println(“<br> <input type=submit value=Press>Upload! </form>”);
}

faccio lo stesso in GetHandler, solo che il nuovo metodo si chiama getQuery e si accoda agli hyperlink.

Ora faccio che estrarre l’interfaccia Handler che contiene i due metodi comuni a PostHandler e GetHandler (duplication is the son of the devil).

A questo punto, all’inizializzazione della servlet :
— definisco i miei handlers (control) e li inserisco in due liste : List<GetHandler> getHandlers e List<PostHandler> postHandlers.
— definisco come apparirà la pagina (presentation) componendola usando i miei elementini, quelli che implementano Actor li posso registrare presso uno degli handlers.
— inizializzo il modello, se necessario.

dentro a doGet metto questo :

for(Handler handler : getHandlers) {
if(handler.isInvolved(request)) handler.handle();
}

page.print(out);

dentro a doPost lo stesso, ma usando postHandlers, manco a dirlo.

Ora… questo non è un MVC calzato e vestito? La servlet è lunga 20 linee scarse (se metto l’inizializzazione in una factory diventano 10), posso aggiungere azioni alla servlet inserendo nuovi handlers nella lista e collegarli a un form o a un hyperlink semplicemente registrando l’elemento all’handler. E non uso nemmeno un file di configurazione!

Se avessi voluto discriminare tramite servlet, e avere uno o due (get e post) handlers per ogni servlet invece che una lista, sarei finito probabilmente in qualcosa di molto simile all’MVC di Struts.

Ne è valsa la pena? Rispetto alla vecchia servlet decisamente si, l’aggiunta di nuove funzionalità ora è del tutto modulare. E’ talmente facile che devo solo creare l’handler e solleticare la mia vena artistica provando vari layout.
Rispetto a un’ipotetica applicazione sviluppata con Struts sin dall’inizio? Forse avrei passato meno tempo a rifattorizzare ( circa il 30% del tempo di sviluppo totale ), ma questo non vale forse un’applicazione molto più leggera, non legata a un framework e che, se devo portare in giro, ha bisogno solo di sè stessa? Dipende, come al solito. Ma a me questa soluzione piace e, cosa molto importante, rispecchia esattamente i miei requisiti, non quelli ipotizzati dall’ideatore di un framework.

Si può migliorare? Penso di si, gli handlers hanno due responsabilità : la discriminazione e l’azione. Se notassi che queste due responsabilità iniziano a divergere la prima mossa sarebbe probabilmente usare lo strategy dentro gli handler e delegare una delle due responsabilità a una certa strategia.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s