Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Applicazione Flash/Flex con il pattern MVC

Scrivere una applicazione Flash/Flex utilizzando il pattern MVC
Scrivere una applicazione Flash/Flex utilizzando il pattern MVC
Link copiato negli appunti

Per tenere ben separata la logica applicativa (la "business logic") di un'applicazione dall'interfaccia utente, sono stati ideati diversi modelli (pattern) architetturali, che ci aiutano nel design delle nostre applicazioni. Nel web ha particolare successo il modello a strati (multi-tier), che vede le applicazioni organizzate in tre livelli logici:

Strato dell'applicazione Funzione
Data logic si occupa dell'astrazione dei dati e dell'interazione con il database
Business logic è la logica applicativa, manipola i dati e li organizza per la presentazione all'utente
Presentation è l'interfaccia grafica con la quale l'utente interagisce e attraverso la quale invia delle istruzioni alla business logic

Il livello di "business logic" risponde alle richieste ricevute dall'interfaccia e comunica i risultati delle elaborazioni all'interfaccia stessa cambiandone lo stato. Una variante di questo modello a strati è l'architettura MVC.

MVC

MVC è l'acronimo di Model View Controller ed è un pattern architetturale formalizzato alla fine degli anni '70 con il linguaggio SmallTalk e diventato di moda negli anni '90, ampiamente diffuso per lo sviluppo di interfacce grafiche di software object-oriented.

Le applicazioni Flex sono notoriamente basate su interfaccia grafica, quindi MVC può venirci in aiuto per organizzare lo sviluppo delle nostre rich internet applications, garantendo la separazione dell'interfaccia grafica dalla business logic.

Anche MVC prevede l'organizzazione dell'applicazione in tre livelli:

Livello Descrizione
Model Incapsula le strutture dati e garantisce l'attribuzione di un significato ai dati grezzi. Siccome il pattern MVC non prevede un livello dedicato all'interazione con la base dati, è di uso comune introdurre nel livello Model la logica per la comunicazione e le richieste al database
View Si occupa della visualizzazione dei dati e della visualizzazione dell'interfaccia utente
Controller Riceve i comandi dallo strato View e modifica lo stato del model in relazione alle richieste ricevute. Contiene la business logic dell'applicazione

Per Flex esistono molti framework MVC. I più importanti sono Cairngorm, un framework realizzato esclusivamente per Flex del cui sviluppo si occupa Adobe, e PureMVC, disponibile anche per altri linguaggi e basato sul design pattern Observer. Questi due framework sono molto complessi e risultano inadatti per lo sviluppo di piccole applicazioni Flex.

In questo articolo vedremo come implementare un semplice pattern MVC adatto allo sviluppo di applicazioni di piccole dimensioni.

Una applicazione "normale"

Per il nostro esempio sviluppiamo una semplice applicazione per inserire e rimuovere elementi da una Datagrid. Per rendere chiari i vantaggi di sviluppare un'applicazione con approccio model-view-controller, costruiamo prima un'applicazione mischiando logica applicativa e interfaccia grafica e, successivamente, la ristrutturiamo separando il codice Actionscript (organizzato in classi) dai component MXML.

Di seguito è riportato il codice dell'applicazione, costituita da un singolo file con estensione .mxml.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
  <mx:Script>
    <![CDATA[
      import mx.utils.ArrayUtil;
      import mx.collections.ArrayCollection;
      [Bindable]
      private var data_list:ArrayCollection = new ArrayCollection();
      public function addUser():void
      {
        if (user_name.text == "" && user_surname.text == "")
          return;
        data_list.addItem(user_name.text + " " + user_surname.text);
      }
      public function delUser():void
      {
        data_list.removeItemAt(userList.selectedIndex);
      }
    ]]>
  </mx:Script>
  <!-- View -->
  <mx:DataGrid x="10" y="102" id="userList" width="302" dataProvider="{data_list}">
    <mx:columns>
      <mx:DataGridColumn headerText="Elenco nominativi" dataField="user"/>
    </mx:columns>
  </mx:DataGrid>
  <mx:Button label="Aggiungi" id="btn_add" click="addUser()" x="236" y="70"/>
  <mx:Button label="Elimina" id="btn_del" click="delUser()" x="164" y="70"/>
  <mx:TextInput id="user_name" x="101" y="10"/>
  <mx:TextInput id="user_surname" x="101" y="40"/>
  <mx:Text text="Nome:" fontWeight="bold" x="51" y="12" />
  <mx:Text text="Cognome:" fontWeight="bold" x="29" y="42"/>
</mx:Application>

I dati su cui lavora l'applicazione sono contenuti nella variabile data_list dichiarata con l'istruzione:

private var data_list:ArrayCollection = new ArrayCollection();

Nello stesso file abbiamo anche inserito il codice che realizza l'interfaccia e le operazioni sui dati.

L'applicazione che abbiamo appena sviluppato è funzionante, ma l'approccio utilizzato non ne garantisce una facile estensione. Il codice che implementa la logica applicativa è mischiato all'interfaccia e non è pensabile sviluppare una grossa applicazione seguendo questo procedimento, in quanto la manutenzione risulterebbe molto dispendiosa.

Basti pensare all'eventuale necessità di utilizzare i dati contenuti nella struttura dati data_list per la visualizzazione in un altro componente: il fatto che la variabile sia dichiarata all'interno del codice che implementa l'interfaccia grafica della Datagrid non garantisce il riutilizzo del codice.

Per questo motivo, si sceglie di complicare la struttura dell'applicazione e di ricorrere ad un'organizzazione del codice in livelli.

Applicare il pattern MVC

Model

Per prima cosa, racchiudiamo i dati della nostra applicazione in una classe che chiamiamo UserListModel. Di seguito è riportato il codice che implementa la classe UserListModel (file UserListModel.as):

package
{
	import mx.collections.ArrayCollection;
	[Bindable]
	public class UserListModel
	{
		private var list:ArrayCollection;
		private static var instance:UserListModel;
		
		public function UserListModel(enforcer:SingletonEnforcer):void
		{
			if (enforcer == null)
				throw new Error( "This class is a Singleton" );
			list = new ArrayCollection();
			instance = this;
		}
		
		public static function getInstance():UserListModel
		{
			if(instance == null)
				instance = new UserListModel(new SingletonEnforcer());
			return instance;
		}

		public function addUser(name:String):void
		{
			list.addItem(name);
		}
		
		public function delUser(index:int):void
		{
			list.removeItemAt(index);
		}
		
		public function getUserList():ArrayCollection
		{
			return list;
		}
	}
}

class SingletonEnforcer {}

Per garantire che ogni modifica effettuata su un membro della classe sia automaticamente trasmessa all'interfaccia, la classe è "marcata" come [Bindable]. Inoltre, siccome nella nostra applicazione abbiamo bisogno di una singola istanza della classe UserListModel, utilizziamo il design pattern creazionale Singleton: si può ottenere un oggetto del tipo UserListModel utilizzando unicamente il metodo statico getInstance(). Per evitare che si possano istanziare più oggetti della classe UserListModel, il costruttore dovrebbe essere dichiarato con scope private, ma ActionScript non lo permette. Per ovviare a questa mancanza, sfruttiamo un oggetto di utility di tipo SingletonEnforcer che deve essere passato come argomento al costruttore: se non è passato un oggetto di tipo SingletonEnforcer viene lanciata un'eccezione.

Controller

Dopo aver creato il model, ci preoccupiamo di scrivere la classe che deve svolgere il ruolo di Controller. In questa classe dobbiamo racchiudere la logica applicativa: creiamo due metodi, uno per l'inserimento e l'altro per la cancellazione di un elemento dalla lista di nomi presente nel model.

Di seguito è riportato il codice che implementa la classe UserListController:

package
{
	public class UserListController
	{
		private var user_list:UserListModel;
		
		public function UserListController()
		{
			user_list = UserListModel.getInstance();
		}

		public function addUserInList(name:String, surname:String):void
		{
			user_list.addUser(name + " " + surname);
		}
		
		public function delUserFromList(index:int):void
		{
			user_list.delUser(index);			
		}
	}
}

Il livello controller dovrebbe contenere e gestire la logica applicativa, che nel caso della nostra applicazione è estremamente semplice. Da notare che la classe UserListController utilizza l'unica istanza della classe UserListModel disponibile all'interno dell'applicazione, ottenibile attraverso il metodo UserListModel.getInstance().

View

L'ultimo passo da compiere per realizzare la nostra applicazione è la realizzazione dell'interfaccia grafica.

L'interfaccia, nel nostro caso, è molto simile a quella che abbiamo già sviluppato in precedenza. Modifichiamo solo gli event handler per gli eventi di click sui pulsanti Aggiungi ed Elimina, che devono ora utilizzare i metodi del controller per cambiare lo stato del model.

Di seguito è riportato il codice che implementa l'interfaccia grafica:

L'oggetto model è definito [Bindable] per garantire l'aggiornamento automatico dell'interfaccia quando ne viene cambiato lo stato.

Come dataProvider della dataGrid è utilizzato il valore di ritorno del metodo getUserList del model, il cui stato cambia quando vengono richiamati i metodi addUserInList e delUserFromList del controller.

Di seguito è riportato l'esempio realizzato:

Conclusioni

La motivazione principale che spinge gli sviluppatori ad organizzare le applicazioni in livelli è quella di garantire la massima coesione e il minimo accoppiamento tra i moduli: ogni classe deve svolgere un insieme definito e limitato di operazioni e deve essere minimizzato il numero di richieste ai metodi di altre classi.

L'applicazione sviluppata è un semplice esempio per capire il funzionamento del pattern MVC, e può essere preso come modello per lo sviluppo di piccole applicazioni. Per applicazioni di grandi dimensioni, invece, è più adatto l'utilizzo di framework MVC quali Cairngorm e PureMVC, che garantiscono un migliore controllo dell'applicazione e un miglior riutilizzo del codice.

Ti consigliamo anche