Articoli Manifesto Tools Links Canali Libri Contatti ?
Linguaggi / Delphi

È possibile rendere light-weight un'applicazione fat-client? (Parte prima)

Abstract
Chi, come me ha programmato in Java e ora si trova a dover programmare in Delphi sa quanto si rimpiange la dinamicità del linguaggio di Sun, quantomeno in termini di introspezione. Ma non tutto è perduto. In questo articolo mi piacerebbe condividere con voi qualche idea riguardo la creazione in Delphi di oggetti visuali a runtime (in particolare le forms) utilizzando tecniche poco comuni che molto si avvicinano al "riflessivo" Java ...
Data di stesura: 07/03/2006
Data di pubblicazione: 14/03/2006
Ultima modifica: 04/04/2006
di Gianantonio Dehò Discuti sul forum   Stampa

Fat Client e DB: un classico ... o un incubo?

La storia si ripete. Sempre. Ognuno di noi ha visto le proprie applicazioni database degradare le proprie performance proporzionalmente all'aumentare della complessità della struttura dei dati e della quantità di forms appartenenti al progetto.

Se poi la struttura del db è disegnata "non proprio bene" o non è perfettamente consona alla quantità e tipologia dei dati che ospita, ecco il "coup de grace". La nostra applicazione soccombe miseramente, dando all'utilizzatore quella spiacevole sensazione di lentezza e scarsa reattività che ci si augura sempre di non provare quando si utilizza un applicativo desktop.

Stiamo parlando di applicazioni win32 client/server realizzate con Delphi, che accedono ai dati via ODBC ad un qualsiasi database relazionale SQL based.

Pensate ad un gestionale: in media è costituito da 50-100 tabelle (database), che necessitano di gestione completa (possibilità di visualizzare, ricercare, inserire modificare e rimuovere dati). Ci si trova pertanto ad avere da cinquanta a cento forms, solo per la gestione dei dati.

Gran parte delle form contiene controlli visuali collegati al database (i cosiddetti controlli "DBAware"): il binding verso le tabelle ed i campi che contengono i dati da gestire sono stati definiti a design time: ciò implica che al verificarsi dell'evento OnDataChange, ogni componente DBAware cerca di recuperare dal database il dato che deve visualizzare.

Con questo scenario, se le form venissero create allo startup dell'applicazione attenderemmo minuti prima di vedere comparire la main form, oltre a causare un consumo smodato della memoria di sistema.

Lazy? No: On Demand!

La soluzione c'è, ovviamente: semplice, efficiente e definitiva.

Dobbiamo evitare che la nostra applicazione crei automaticamente le form allo startup, e crearle noi programmaticamente, solo quando e se ci servono.

Selezioniamo il menu [Project->Options] e nel tab Forms togliamo dal pannello "Auto create forms" tutte le forms presenti (eccetto la main form...).

E per creare la form che ci serve operiamo come segue:

Con la semplice esplorazione della collezione Forms contenuta nella variabile globale Screen() cerchiamo (per nome) se c'è già la form che dobbiamo aprire e se non la troviamo, la costruiamo, come indicato nell'esempio

  1. for i := 0 to Pred(Screen.FormCount) do begin 
  2. 	if uppercase(Screen.Forms[i].Name) = "FormNameToSearchFor" then 
  3. 	begin 
  4. 		found := TForm(screen.Forms[i]); 
  5. 		break; 
  6. 	end; 
  7. end; 
  8. if not assigned(found) then 
  9. 	begin 
  10. 		//codice per la creazione della form 
  11. 	end; 
  12. end; 

In alternativa è anche possibile (e questa è la soluzione che normalmente adotto nelle mie applicazioni), creare a distruggere la form ogni volta che la si deve usare. Lo si può fare automaticamente usando l'evento OnClose della form, con la seguente istruzione:

  1. procedure TForm1.FormClose(Sender: TObject); 
  2. begin 
  3.      self.release; 
  4. end; 

Se adotterete questa soluzione non sarà necessario esplorare la variabile globale Screen in quanto la form sarà sempre da costruire.È chiaro che questa metodologia ci costringe ad avere un dispatcher che, dato il nome di una form ci restituisce un'istanza di quella form, ma vi garantisco che i vantaggi sono innumerevoli.

Reflective Factory in Delphi

Il programmatore attento si pone subito la domanda corretta: come possiamo ottenere a runtime l'istanza di una form che abbiamo progettato e costruito a design time e di cui conosciamo solo il nome? Normalmente Delphi ci "regala" il codice per avere le istanze di tali form. Una breve sbirciatina al file di progetto <mioProgetto>.dpr ci rivela come ciò avviene:

    Application.CreateForm(TForm1, Form1);

A noi ciò non va bene in quanto i due parametri da passare al metodo CreateForm sono:

il primo di tipo TFormClass, ed il secondo un untyped param che serve per contenere il reference della classe creata.

Un altro modo per creare una form è quello di "costruirla" invocando il suo costruttore Create(AOwner: TComponent), ma anche in questo caso il nome della form non è sufficiente in quanto non possiamo certo creare un'istanza di un oggetto a partire da una stringa scrivendo del codice tipo:

    'TForm1'.create(Application);

Peggio ancora un pessimo e poco efficiente codice come il seguente

  1. if FormNameToSearchFor = "ArticoliForm" then  
  2. begin 
  3.    Application.CreateForm(TArticoliFrm, ArticoliFrm); 
  4. end; 
  5. if FormNameToSearchFor = "OrdiniForm" then  
  6. begin 
  7.    Application.CreateForm(TOrdiniFrm, OrdiniFrm); 
  8. end; 
  9. //.... eccetera!!!!! 

Ci serve quindi il modo per cercare il tipo di una classe dal suo nome. Come sempre l'ottima documentazione di Delphi ci viene in aiuto. La funzione FindClass è quello che ci serve:

  1. function CreateObject(FormNameToSearch: string): TObject; 
  2. var 
  3.   clazz: TFormClass; 
  4. begin 
  5.   result := nil; 
  6.   clazz := TFormClass(FindClass(FormNameToSearch)); 
  7.   if assigned(clazz) then 
  8.   begin 
  9.     //trovato, creo la classe 
  10.     result := clazz.Create(Application); 
  11.   end 
  12.   else begin 
  13.     //nome non trovato 
  14.     .... 
  15.   end; 
  16. end; 

Il cast a TFormClass è necessario, in quanto FindClass ritorna un generico TPersistentClass.

Questo codice funziona solo se le form che cerchiamo sono state preventivamente registrate con la funzione

    RegisterClass(AClass: TPersistentClass).

che permette allo streaming system di Delphi di "essere cosciente" dell'esistenza della form (non dimenticate che abbiamo eliminato la creazione automatica delle forms).

Finalmente abbiamo ciò che ci serve. L'istanza a runtime di una specifica form che abbiamo disegnato con l'IDE.

È evidente che quanto detto è solo uno spunto. Ma ora abbiamo tutti gli strumenti per poter generalizzare, centralizzare e lasciare che il numero di form cresca a dismisura, visto che durante l'esecuzione del nostro applicativo le form presenti in memoria saranno solo quelle effettivamente in uso. Il consiglio che vi do è quello di fare in modo che tutte le form della vostra applicazione discendano dallo stesso padre, che è una VOSTRA classe:

[Figura 1]

Figura 1

In questo modo potrete centralizzare tutte le operazioni comuni nella classe padre, come per esempio l'aspetto ed i colori o addirittura la manipolazione e il salvataggio dei dati.

Ma questa è un'altra storia ...

Informazioni sull'autore

Gianantonio Dehò, lavora da 8 anni nel campo dell'IT. Recentemente ha messo a frutto l'esperienza maturata nell'ambito dei sistemi qualitá, ideando e realizzando precISO®, software per la gestione e manutenzione dei sistemi qualitá secondo ISO9000, distribuito dall'omonima societá.

È possibile consultare l'elenco degli articoli scritti da Gianantonio Dehò.

Altri articoli sul tema Linguaggi / Delphi.

Discuti sul forum   Stampa

Cosa ne pensi di questo articolo?

Discussioni

Questo articolo o l'argomento ti ha interessato? Parliamone.