Gli Oggetti e le Classi

Da Gambas-it.org - Wikipedia.

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. [ Nota 1 ]

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. [ Nota 2 ] 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 assumono 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 sta 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 in 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 [ Nota 3 ] 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.




Note

[1] Bisogna ricordare che un Oggetto non è altro che una Classe istanziata.

oggetto = New Classe

laddove la variabile oggetto è appunto un Oggetto.

Con parola-chiave New viene creato un Oggetto, appartenente ad una specifica Classe, allocando automaticamente la necessaria quantità di memoria per esso richiesta e che così rappresenta la struttura interna dell'Oggetto medesimo.
Possiamo dire che quando viene usata la parola-chiave "New" con un tipo primitivo:

b = New Byte[];

è come quando nel linguaggio C si stesse usando la funzione "malloc( )" istanziando una grandezza di "sizeof(char)":
Pertanto se se si scrive

b = New Byte[16];

è uguale in C a:

char *c = malloc(sizeof(char) * 16);


In Gambas gli Oggetti sono entità individuali concrete di un insieme più ampio, di un Universale che li trascende, chiamato Classe, e che rappresenta per essi un Modello, una Categoria assoluta delle loro caratteristiche (Proprietà, Metodi, Eventi) possedute che li distinguono dagli Oggetti appartenenti ad altre Classi.
Pertanto gli Oggetti, singolarità di una Classe, per esistere devono, come ogni entità concreta immanente, essere creati.

Una Classe concettualmente è ciò che è comune a più realtà individuali e concrete. Usando una espressione del filosofo Aristotele, possiamo dire che la Classe è «Ciò che naturalmente si predica di più cose». Essa è un universo, un "tutto" che tiene insieme più elementi particolari, gli Oggetti, uniti da peculiarità cumuni. La Classe, dunque, è un insieme che raggruppa molti elementi individuali (gli Oggetti) che posseggono caratteristiche comuni. Tali caratteristiche in Gambas possono essere:

  • Proprietà;
  • Metodi;
  • Eventi.

La Classe è il modello contenente specifiche caratteristiche (Metodi, Proprietà ed Eventi), al quale fanno riferimento singoli elementi: gli Oggetti. In tal senso gli Oggetti appartengono ad una Classe, ossia sono la rappresentazione concreta delle caratteristiche e delle potenzialità di una Classe. Essi sono gli elementi capaci di fornire concretamente al programmatore ed all'utente le funzionalità della Classe, alla quale essi appartengono. Infatti, la Classe in sé non può essere utilizzata sic et simpliciter all'interno di un codice, giacché essa solo in astratto possiede alcune specifiche funzionalità. Tali capacità possono essere concretamente utilizzate solo attraverso elementi individuali, gli Oggetti, che alla Classe (astratta) fanno riferimento.
La Classe specifica, dunque, le sue caratteristiche e funzionalità, le quali potranno essere utilizzate e gestire mediante gli Oggetti.
Tali funzionalità e caratteristiche (Metodi, Proprietà ed Eventi) di una Classe sono tra loro coerenti ed organici alle specificità ed alle potenzialità della Classe, in modo tale che la Classe possa svolgere i compiti, per i quali fu creata.
La Classe è l'elemento astratto, nella quale sono definite determinate funzionalità, compiti e caratteristiche (Metodi, Proprietà ed Eventi); mentre l'Oggetto è l'elemento concreto, virtualmente materiale, utilizzabile nel codice, e che rende possibile l'uso delle potenzialità della Classe, alla quale esso appartiene. In sostanza la Classe è utilizzabile solo attraverso gli Oggetti che ad essa si riferiscono.
La dinamicità della Classe è dovuta anche a questa sua capacità e necessità di esteriorizzarsi, di concretizzarsi in una possibile molteplicità di elementi (gli Oggetti) che ad essa si riferiscono, e che la rendono così effettivamente utilizzabile nel codice e, dunque, nel programma.

Per comprendere a pieno il concetto di "istanza" di una Classe, è opportuno comprendere l'etimo della parola, che proveniendo dal latino "in + sto", vuol significare "stare in", e quindi "stare nel (mondo)", "starci", "esistere nella (realtà)". Una Classe in quanto modello non esiste nel/sul mondo, ossia concretamente, è semplicemente un predicato astratto, un paradigma. Se la Classe si fa concreta, si in-stanzia, ossia si in-pone nella realtà concreta del programma, diventa gestibile in modo effettivo, virtualmente tangibile: diventa insomma un "Oggetto". L'Oggetto si pone, sta nel mondo del programma informatico con una sua individualità, definita e condizionata da funzionalità e caratteristiche che rimandano necessariamente alla sua Classe di appartenenza.


[2] Minisini, rispondendo nella Mailing List ufficiale di Gambas, ha così brevemente definito il Modulo:

"A module is just a class whose all methods and properties are static.
It's a thing coming from Visual Basic.
It's like a class, except everything is static.
It's static, that means it cannot be instantiated."


[3] La Parola Chiave è una parola riservata del linguaggio di programmazione, che non può essere utilizzata diversamente da ciò per cui è stata concepita.
Per esempio nella riga:

Dim variabile As Integer

la parola "Dim" è una parola chiave, mentre non lo è la parola "variabile".