Gli Oggetti e le Classi

Da Gambas-it.org - Wikipedia.
Versione del 26 set 2008 alle 04:53 di 213.203.155.10 (Discussione)

(diff) ← Versione meno recente | Versione attuale (diff) | Versione più recente → (diff)

Premetto che non ho intenzione, in questo articolo, di avviare una discussione teorico/pratica sul concetto di oggetto o classe, ma solo evidenziare alcuni aspetti relativi alla programmazione di tali elementi in Gambas.

In Gambas, il concetto e la logica degli oggetti è stata fortemente implementata; infatti, nel linguaggio base esistono due categorie: classe e modulo. Queste due categorie, nella realtà, hanno pochissime differenze, nel senso che in ogni caso sono basate sul concetto di oggetto.

La differenza sostanziale tra queste due implementazioni, è che in definitiva, il modulo non è altro che un contenitore di funzioni (o variabili), che vengono referenziate agendo sul modulo stesso. Il concetto di base, però, è identico a quello della classe, tranne per il fatto che un modulo, a differenza di una classe, non può essere creato dinamicamente ovvero, una volta stabilita la sua funzione all'interno di un'applicazione, e definiti i suoi metodi interni, l'oggetto rimane statico all'interno del programma. Il discorso è molto simile, se non completamente analogo, alle classi statiche contenute nelle librerie di base di Gambas, come ad esempio "gb".

All'interno di un progetto, si ha la possibilità di creare, sia nuove classi, che nuovi moduli; il nome dei file assumo l'estensione ".module" per i moduli, e ".class" per le classi. Discorso leggermente diverso per gli oggetti grafici (esempio le Form), che creano un'ulteriore file ".form", contenente la definizione delle caratteristiche grafiche dell'oggetto e degli elementi inclusi in esso.

A differenza di una classe, un modulo (come già detto) non può essere creato (esiste, e basta!), per cui non alcun costruttore, e di conseguenza nessun distruttore (vedi _new() e _free() in Metodi nascosti), non risponde ad alcun evento (anche dal fatto che non è un oggetto grafico), e non può gestire direttamente eventi (esempio un Timer).

Esempi:


 'Esempio 1: creazione di una classe dinamica
 DIM hWin AS NEW Window()
 'Esempio 2: referenziazione di una classe dinamica
 DIM hWin2 AS Window
 hWin2 = hWin
 'Esempio 3: utilizzo di una funzione modulo
 DIM value AS Float = ModUtil.CalcoloPerc(10)

Negli esempi sono mostrati alcuni modi di utilizzo, sia riguardo le classi, sia riguardo un modulo:

a) Nell'esempio 1 viene creato un nuovo oggetto di tipo Window, e referenziato dalla variabile hWin.

b) Nell'esempio 2 viene definita una variabile di tipo Window, che viene poi associata all'oggetto precedente hWin; in questo caso non viene creato un nuovo oggetto, ma solo un riferimento a quello già esistente. In questo caso, l'eliminazione della variabile hWin2 non distruggerà l'oggetto hWin, ma toglierà solo il suo riferimento; in ogni caso la variabile precedente hWin, ovvero il vero riferimento all'oggetto, manterrà sempre l'oggetto in memoria. L'eliminazione dell'oggetto dovrà essere eseguita agendo sul proprio metodo ".Delete()" (se presente), o eliminando la stessa variabile hWin. E' da notare che, se viene cancellata la variabile originale hWin, e viene mantenuta hWin2, l'oggetto resta comunque in memoria, dato che esiste comunque un suo riferimento valido. Questo giochetto può essere molto utile in alcuni casi, ma è necessario fare molta attenzione a non lasciare appesi riferimenti inutili.

c) Nell'esempio 3 viene utilizzata una funzione interna al modulo ModUtil: CalcoloPerc(). A parte la sua funzione specifica (è solo un esempio), il concetto è visibilmente diverso dall'utilizzo di una classe. L'uso della funzione CalcoloPerc() deve avvenire passando per il riferimento al modulo ModUtil, ma l'utilizzo è immediato, come anche il valore di ritorno.

Come per classi, i moduli possono contenere variabili interne, statiche (STATIC), private (PRIVATE) e/o pubbliche (PUBLIC), e anche costanti (CONST). Dipedentemente dalla loro dichiarazione, l'uso è identico a quello di una proprietà di classe, tranne per il fatto che la proprietà (o variabile) non viene creata, ma esiste nello stesso ambito del modulo.


' Gambas module file
.
'*** Modulo ModUtil ***
.
PUBLIC FUNCTION CalcoloPerc(value AS Float) AS Float
  ...
  ...
  ...
END

Nell'esempio viene mostrato come può apparire il codice del modulo ModUtil, e il suo metodo (o funzione) interno.


' Gambas class file
.
'*** Classe MyWindow ***
.
PUBLIC SUB Form_Open()
  ...
  ...
  ...
END

In quest'altro esempio viene mostrato il codice di una classe Window.

Come si può notare, la struttura dei due file è molto simile (se non addirittura uguale); Gambas da parte sua, inserisce automaticamente un marcatore ad inizio file, ad evidenziare il tipo di file che si stà trattando. Ricordo che la modifica di questa nota non è permessa, e in ogni caso Gambas la reinserisce automaticamente.


Parliamo ora degli oggetti (o classi)...

Come già descritto, le classi differiscono dai moduli, principalmente per il fatto che possono e devono essere creati e istanziati. Oltre a questo, hanno alcune particolarità, che ne semplificano l'uso, e oltre tutto devono obbligatoriamente possedere, per poter gestire un oggetto un maniera umana. A parte i propri metodi e le loro proprietà, che dipendono dal tipo di oggetto e dal tipo di funzione che deve svolgere, esistono delle parole chiave, con lo scopo di fornire elementi utili al loro utilizzo.



ME - Riferimento a se stesso

La parola chiave ME è messa a disposizione per poter puntare all'oggetto, all'interno di se stesso; cosa che altrimenti non sarebbe fattibile. Come per i moduli, che si referenziano esternamente, dichiarando il nome del modulo stesso, per le classi, all'interno dei loro metodi, si può avere un riferimento allo stesso oggetto con ME.


' Gambas class file
.
'*** Classe Form ***
.
PUBLIC SUB Form_Open()
  ...
  ME.Caption = "Nome della Form"
  ...
END

Nell'esempio viene impostato il testo che comparirà sulla testata della Form; come si può notare, il riferimento alla Form (ovverro a se stessa), viene fatto da ME. Il punto che segue indica al compilatore di Gambas, che il nome che seguirà sarà un riferimento ad una proprietà o un metodo interno alla classe (in questo caso Caption).


' Gambas class file
.
'*** Classe Form ***
.
PUBLIC SUB Form_Open()
  ...
  WITH ME
    ...
    .Caption = "Nome della Form"
    .X = 0
    .Y = 0
    ...
  END WITH
  ...
END

La sintassi di quest'altro esempio è la stessa, la differenza stà nel fatto di aver utilizzato la sintassi WITH...END WITH. Questo tipo di sintassi permette di stabilire a priori che si vogliono modificare solo elementi della classe, per cui non c'è più bisogno della parola ME, per cui i riferimenti verranno fatti iniziando i nomi delle proprietà di classe con il punto. E' sottinteso che questa logica è valida anche per i metodi, non solo per le proprietà:


' Gambas class file
.
'*** Classe Form ***
.
PUBLIC SUB Form_Open()
  ...
  WITH ME
    ...
    .Caption = "Nome della Form"
    .X = 0
    .Y = 0
    ...
    .Init()
    ...
  END WITH
  ...
END
...
...
...
PUBLIC SUB Init()
  ...
END

Come si può notare, all'interno di WITH, è presente anche la chiamata al metodo Init(), che appartiene alla stessa classe.



SUPER - Riferimento alla classe superiore

Un'oggetto (o classe) può essere derivata da un'oggetto (o classe) superiore; in questo caso la nuova classe assume tutte le carattersitiche PUBLIC della classe padre, e la possibilità di espanderne le proprietà e/o specializzarne la funzione. Un esempio è la classe CheckBox, che deriva dalla classe Control.


' Gambas class file
.
'*** Classe MyWindow ***
.
INHERITS Window
.
PUBLIC SUB Window_Open()
  ...
  WITH ME
    ...
    .Caption = "Nome della Window"
    .X = 0
    .Y = 0
    ...
  END WITH
  ...
END

Nell'esempio è mostrato il codice della classe MyWindow, che deriva dalla classe base Window di Gambas. La nuova classe assume tutte le caratteristiche PUBLIC della classe Window, comprese le proprietà, i metodi e i gestori di evento. A questo punto non è necessario riscriverne le proprietà, dato che sono già impostate a livello di classe padre; al più possiamo aggiungerne di altre, specializzando la funzione di questo nuovo oggetto. In realtà è anche possibile sovrascrivere una specifica funzionalità, rispetto alla classe padre; in questo caso è sufficiente ricreare il metodo, scrivendoci dentro il codice desiderato. Ma come facciamo a interagire con la classe padre? Semplice, in questo caso ci viene in aiuto un'altra parola chiave: SUPER. Ogni qualvolta entriamo in un metodo, che abbiamo sovrascritto per nostra necessità, possiamo chiamare il metodo della classe padre:


' Gambas class file
.
'*** Classe MyWindow ***
.
INHERITS Window
.
PUBLIC SUB Resize()
  ...
  SUPER.Resize()
  ...
END

Nell'esempio viene utilizzato il metodo Resize, nel quale abbiamo scritto codice adatto ai nostri scopi, e poi eseguiamo anche la stessa funzione nella classe padre, che potrebbe essere necessaria, in questo caso, a risolvere e attuare eventuali aggiustamente alla Window.

Nello stesso esempio è presente anche un'altra parola chiave: INHERITS; ma questo è descritto in modo esplicativo in Inherits.

Per ogni oggetto, sia che venga creato da noi, sia che faccia parte delle librerie standard di Gambas, esistono dei metodi, nascosti, che possono essere utilizzati per condizionare la creazione, o meno, dell'oggetto stesso. Questi metodi sono descritti in Metodi nascosti.