Web applications: scrivere codice ben strutturato con i Javascript Design Patterns
Nel processo di strutturazione di qualsiasi software, indipendentemente dal linguaggio di programmazione con cui viene realizzato, bisogna affrontare inevitabilmente molti problemi comuni che rendono più difficile il nostro percorso.
I javascript design patterns ( o schemi di progettazione ), sono una lista di soluzioni architetturali per risolvere dei problemi ricorrenti nella realizzazione di un software.
Perché ne parlo
L’obiettivo di questo articolo è quella di fornire una delucidazione di base sui design Pattern Javascript, perché li reputo una risorsa fondamentale per la creazione di applicazioni web, in quanto rappresentano soluzioni derivanti dal risultato degli studi di numerosi sviluppatori, i quali hanno già affrontato prima di noi problemi comuni, e che ci possono far risparmiare ore preziose e numerosi mal di testa!
Oltre a quello di rappresentare l’esperienza di numerosi programmatori, i pattern ci aiutano anche a mantenere i nostri script più leggeri e puliti evitando inutili ripetizioni di codice, e ci permettono di concentrare le nostre risorse sulla qualità del prodotto, più che sulla sua architettura.
I patterns sono quindi di soluzioni già testate e performanti che possono essere riutilizzate facilmente.
Tuttavia non facciamo l’errore di percepire il pattern come una soluzione esatta al nostro problema, ma pensiamo ad esso come uno schema logico sul quale appoggiarsi per ridurre i nostri problemi strutturali al minimo.
I Pattern per la creazione di applicazioni Javascript
Sui vari tipi di patterns e su quale usare a seconda delle esigenze ci sarebbero un infinità di argomenti di cui parlare e da approfondire, ma con questo post voglio concentrarmi maggiormente su quelli con cui ho avuto modo di lavorare e che secondo me possono essere molto indicati per creare applicazioni Javascript e HTML su piccola e larga scala.
Di seguito elenco per ordine di semplicità i tre pattern più conosciuti ed utilizzati; tieni presente che essi sono spesso usati insieme.
1 – Il pattern a moduli
Si tratta di un modello-base di sistema che ci aiuta a mantenere tutti i vari elementi e unità del nostro codice separati e ben organizzati. Una delle principali caratteristiche alla base della sua logica è senz’altro la presenza delle closure (o chiusure), ovvero la possibilità di usare, oltre a quelle pubbliche, delle variabili o funzioni che sono accessibili in modo privato solo all’interno di quel modulo; così facendo è possibile dividere la parte di codice che ci serve solo per un determinato scopo dal resto dell’applicazione (evitando anche possibili sovrascrizioni di nomi di variabili già esistenti).
La variabile di modulo ritorna un’istanza di oggetto e tutta la serie di metodi e variabili pubbliche in esso dichiarate. Questo tipo di pattern è molto utile per esempio nel caso di un tipo di schema logico in cui ogni parte della propria interfaccia viene eseguita separatamente dalle altre e funziona perlopiù autonomamente.
Ecco un esempio di un modulo, che ritorna un oggetto pubblico:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
var footballTeamModule = (function() { var teamPlayers = [], //oggetto privato sortFunction = function(a,b){ //oggetto privato return (a.role < b.role) ? -1 : (a.role > b.role) ? 1 : 0; }; //oggetti pubblici return { addPlayer: function(values) { teamPlayers.push(values); }, getPlayersNumber: function() { return teamPlayers.length; }, getRoles: function(){ teamPlayers.sort(sortFunction); var l = this.getPlayersNumber(),r=[]; for(var i=0;i<(l-1);i++){ if(teamPlayers[i].role != r[r.length-1]) r.push(teamPlayers[i].role); } return r; } } }()); |
Ora potremmo interagire con il modulo come segue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
footballTeamModule.addPlayer({name:'Marco', role:'Difensore'}); footballTeamModule.addPlayer({name:'Giovanni', role:'Portiere'}); footballTeamModule.addPlayer({name:'Luca', role:'Difensore'}); footballTeamModule.addPlayer({name:'Andrea', role:'Attaccante'}); footballTeamModule.addPlayer({name:'Luigi', role:'Difensore'}); footballTeamModule.addPlayer({name:'Matteo', role:'Centrocampista'}); footballTeamModule.addPlayer({name:'Lorenzo', role:'Centrocampista'}); footballTeamModule.addPlayer({name:'Silvio', role:'Attaccante'}); console.log("Number of players:" + footballTeamModule.getPlayersNumber()); console.log(footballTeamModule.getRoles()); //mentre invece non sarà possibile accedere da qui alla variabile teamPlayers console.log(footballTeamModule.teamPlayers); // questa riga stamperà nella consol il valore 'undefined' |
2 – Il pattern a facciate (Facade pattern)
Il facade pattern o pattern facciata è un tipo di schema in cui il client si preoccupa soltanto di richiamare un’interfaccia più semplice (appunto la facciata), la quale gestisce l’accesso a delle sotto interfacce più complesse, ognuna diversa dall’altra.
Possiamo dire quindi che il facade pattern è un modo per nascondere la vera complessità di una struttura sotto una maschera semplificata.
Questo tipo di pattern è molto diffuso nella maggior parte dei frameworks Javascript (come per es. jQuery), che solitamente sono costituti da un elevato numero di funzionalità e sotto funzionalità, e che vogliono limitare la nostra l’interazione alla sola “facciata” ed alle sue risorse pubbliche (è il caso delle API).
Generalizzando, possiamo pensare al Facade Pattern come un insieme di più moduli, tutti gestiti e richiamati, da un modulo-facciata principale:
Di seguito uno schema logico del pattern seguito da un’esempio Javascript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var playerModule = (function() { var privateObj = { name: "Francesco", role: "Portiere", getName : function() { console.log('Player:' + this.name); }, getRole : function() { console.log('Role:' + this.role); }, setRole : function( val ) { this.role = val; }, dribble : function() { console.log('is dribling'); }, kick: function(){ console.log('is kicking a penalty kick'); } }; return { facade : function( arguments ) { privateObj.setRole(arguments.role); privateObj.getName(); privateObj.getRole(); if ( arguments.kick ) { privateObj.kick(); } } } }()); playerModule.facade({kick: true, role: "Difensore"}); //il risultato del log sarà : // Player: Francesco // Role: Difensore // is kicking a penalty kick |
3 – Il pattern mediatore (Mediator pattern)
E’ il più complesso fra i vari tipi di patterns, in quanto presenta un maggior numero di livelli, e si preoccupa di organizzare le modalità con le quali le varie parti della struttura interagiscono tra di loro. In termini più pratici questo pattern presenta un Controller, ovvero un oggetto intermediario che controlla tutte le e comunicazioni tra i vari moduli, evitando che interagiscano direttamente fra di loro. Viene usato su modelli progettuali in cui la comunicazione diretta tra i moduli può risultare particolarmente complessa.
Uno dei vantaggi principali di questo tipo di pattern è l’elevato livello di flessibilità, costituito in primis dal fatto che ogni modulo può eseguire le sue operazioni in modo del tutto indipendente da quelle di un altro modulo.
Di seguito schema logico ed esempio con codice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
var playersMd = (function(){ var addEvent = function(event, fn){ if (!playersMd.events[event]) playersMd.events[event] = []; playersMd.events[event].push({ context: this, callback: fn }); return this; }; var callEvt = function(event){ if (!playersMd.events[event]) return false; var args = Array.prototype.slice.call(arguments, 1); for (var i = 0, l = playersMd.events[event].length; i < l; i++) { var evt = playersMd.events[event][i]; evt.callback.apply(evt.context, args); } return this; }; return { events: {}, addEvent: addEvent, callEvt: callEvt, addToTeam: function(obj){ obj.addEvent = addEvent; obj.callEvt = callEvt; } }; }()); var obj = { name: 'Francesco', role: "Portiere"}; playersMd.addToTeam(obj); obj.addEvent('changePlayerName', function(argument){ var previousName = this.name; this.name = argument; console.log("Player name '"+previousName+"' has changed in '"+this.name+"'"); }); obj.addEvent('changePlayerRole', function(argument){ var previousRole = this.role; this.role = argument; console.log("Player role '"+previousRole+"' has changed in '"+this.role+"'"); }); obj.callEvt('changePlayerName', 'Alberto'); obj.callEvt('changePlayerRole', 'Attaccante'); console.log(JSON.stringify(obj)); |
I tre pattern combinati fra loro
I tre tipi di patterns sopra elencati sono spesso combinati fra loro, in modo da poter raggiungere un grado di flessibilità molto elevato, come nel caso di numerosi frameworks per la creazione di applicazioni web (un esempio lampante è rappresentato da Backbone.js).
Ecco lo schema logico di una possibile combinazione tra i 3 tipi di patterns:
Conclusioni
I tre modelli sopra descritti rappresentano solo l’idea generale della vasta lista di Design Patterns a cui possiamo affidarci per la creazione delle nostre web apps, tuttavia trovo che siano quelli che meglio possono essere adattati ai più diversi tipi di esigenze.
Nei prossimi articoli dedicati a questo argomento, affronteremo anche diversi aspetti che costituiscono le varie fasi per la creazione di applicazioni web.
Giacomo Freddi
Web Designer Freelance e Developer, si occupa del design e dello sviluppo di applicazioni web dal 2008, come molti freelance è abituato a gestire più ruoli e spaziare su più campi, ma la sua passione principale è quella della creazione di interfacce front-end e back-end utilizzando codice html5 e css3. Adora usare pattern MVC per i suoi Javascript.
Pingback: Creare applicazioni web: il pattern Model-View-Controller ed i principali MVC Javascript Frameworks | upCreative()
Pingback: Creare applicazioni HTML5, CSS3 e Javascript | upCreative()
Pingback: MVC Backbone per creare applicazioni web HTML5 | upCreative()
Pingback: Tutorial Backbone.js: come realizzare un orologio CSS3 e javascript con animazione - Lobae Design()
Pingback: HTML5 Mobile App: l’importanza del framework MVC | upCreative()