Il modello a oggetti
1. Oggetti e classi
Un'
oggetto (
object) in
Gambas è una
struttura di dati atta a contenere proprietà (
properties), variabili (
variables), metodi (
methods) ed eventi (
events).
Gli oggetti sono accessibili per riferimento (by reference), per esempio, è possibile usare un puntatore su di essi, per ottenere una variabile (
variabile), la quale contiene l'indirizzo in memoria della struttura dell'oggetto stesso.
 |
E' possibile conoscere stampandolo, l'indirizzo di un'oggetto (object) usando l'istruzione PRINT.
DIM aStr AS NEW String[]
PRINT aStr
(String[] 0x80dccf8)
|
La struttura dei dati di un'oggetto (
object) è definita da una
classe (
classe).
1.1. Classi
Ogni oggetto
Gambas ha una
classe che lo descrive in tutte le sue proprietà pubbliche, i suoi metodi e i suoi eventi.
La classe è lo stampo con cui si creano gli oggetti, ed è fondamentale in Gambas. Esse sono il pilastro su cui Gambas si erge.
Una classe può essere
statica in questo caso tutti i suoi membri sono
statici (vedi oltre). In
Gambas, una
classe statica, è chiamata anche
modulo.
Una classe statica non può essere istanziata, si creerebbe una variabile che fa riferimento ad un'oggetto già dichiarato il che non ha alcun senso. Gambas per facilitare la vita al programmatore, include all'avvio dell'interprete classi che non sono facenti parte strettamente del linguaggio stesso (il basic) ma, essendo di uso molto comune, le carica come predefinite. Questo fa sembrare appunto una classe statica come facente parte del linguaggio ma non è così. Questa facilitazione è in contrapposizione con altri linguaggi in cui qualsiasi classe non facente parte del linguaggio deve essere necessariamente istanziata prima del suo utilizzo.
Esempio
La classe
System è una classe statica, tutti i suoi metodi sono di conseguenza anch'essi statici, non è possibile quindi creare un oggetto da
System. Viceversa in qualsiasi punto del programma la si vorrà utilizzare essa sarà disponibile un pò come fosse un modulo.
Una
virtual class è una pseudo-classe nascosta che non è possibile manipolare esplicitamente.
1.2. Properietà, metodi e variabili
Le proprietà e i metodi, permettono di manipolare i dati contenuti all'interno nella struttura della classe.
Una proprietà, un metodo o una variabile, possono essere
statici:
- Una variabile statica può essere condivisa da tutte le istanze della stessa classe.
- Una proprietà statica o un metodo possono solo modificare variabili statiche.
Un metodo o una variabile possono essere o
pubblici o
privati, mentre una proprietà, e
SEMPRE pubblica.
Quando si dichiara un simbolo come
private esso può essere usato solo all'interno della classe stessa dove è dichiarato.
Viceversa un simbolo dichiarato
public può essere utilizzato ovunque, purché si disponga ovviamente di un riferimento che punta all'oggetto in questione.
1.3. Riferimenti
I riferimenti, sono indirizzi di memoria dove l'oggetto comprensivo dei suoi dati, viene allocato. Quindi ad ogni istanza di un'oggetto viene occupata una distinta locazione di memoria. Questo porta a consumare più memoria rispetto alla tradizionale programmazione procedurale, ed è per questo che i moderni linguaggi orientati agli oggetti dispongono di un meccanismo automatico più o meno sofisticato per liberare la memoria non più utilizzata. Questo meccanismo si chiama
GC che sta a
garbage collector (tradotto significa colui che raccoglie la spazzatura).
Gambas non implementa un vero e proprio
GC come in Java ma più semplicemente un'oggetto contatore di riferimenti e più precisamente:
Ogni volta che si istanzia un'oggetto esso (il GC) viene incrementato ed ogni volta che esso viene rilasciato viene decrementato. Quando questo contatore torna a zero vuol dire che non c'è più nessuna variabile che punta ad esso. A questo punto, si ritiene che l'oggetto non sia più utilizzato e la memoria viene liberata.
1.4. Oggetti non validi
Un'oggetto può essere definito come
non valido (
invalid). Questo per esempio, quando si cerca di fare riferimento ad un'oggetto che è stato distrutto di cui è rimasto il link (un orfano) che
Gambas non ha potuto gestire.
Cercando di utilizzare un'oggetto non valido (
object) si solleva un errore.
1.5. Metodi speciali
I metodi speciali, sono metodi nascosti il cui nome comincia con un carattere di sottolineatura, essi sono chiamati dall'interprete nelle seguenti situazioni:
- Quando un'oggetto viene creato.
- Quando un'oggetto viene rilasciato.
- Quando una classe viene caricata.
- Quando la classe viene scaricata.
- Quando si usa un'oggetto se questo è una matrice (array).
- Quando un'oggetto viene enumerato.
- Quando si usa l'oggetto ed esso è una funzione.
- Quando si cerca di utilizzare dei metodi o delle proprietà di un'oggetto sconosciuto.
Fare riferimento a
Metodi speciali e
/api/cat/special per ulteriori informazioni.
2. Eventi e osservatori
2.1. Eventi
Gli eventi, sono segnali trasmessi da un'oggetto quando succede qualcosa al suo interno.
Se un'oggetto solleva un'evento, vi sarà un riferimento all'osservatore o al suo oggetto padre.
L' osservatore è anch'esso un'oggetto che implementa l'aggancio agli eventi, esso è in definitiva un metodo pubblico, che viene chiamato ogni volta che viene sollevato un'evento.
Per impostazione predefinita, l'osservatore è l'attuale oggetto in cui viene dichiarato il neo oggetto istanziato.
Per essere sollevato, un evento di un'oggetto esso deve avere un nome. Questo nome di evento viene assegnato all'oggetto durante al sua creazione con l'istruzione
NEW tramite la parola chiave
AS questo nome sarà come prefisso per tutti i collegamenti agli eventi di quell'oggetto.
Esempio
Questo esempio, crea un controllo bottone (
Button) che potrà lanciare eventi.
DIM hBottone AS Button
hBottone = NEW Button(ME) AS "BottoneEventoNome"
Se nessun nome viene specificato con la parola chiave
AS, allora l'oggetto
non potrà lanciare alcun evento!.
2.2. Blocco degli oggetti
Un'oggetto, può essere bloccato in modo da impedire la capacità di sollevare eventi, e può successivamente essere sbloccato per consentire di nuovo il loro sollevamento. Il poter bloccare gli eventi è utile in fase di inizializzazione di un'oggetto, quando per esempio l'impostazione di proprietà, potrebbe sollevare degli eventi destinati a impostare proprietà che in quel momento non sono ancora state definite o peggio che fanno riferimento a se stessa chiamandosi ricorsivamente fino al raggiungimento della fine dello stack mandando in crash l'applicativo.
Per ulteriori informazioni, fare riferimento a
Object.Lock e
Object.Unlock metodi.
Alcuni eventi possono essere cancellati dal gestore usando l'istruzione
STOP EVENT, l'effetto di questa cancellazione dipende dal tipo di evento.
 |
Attenzione! un'oggetto viene automaticamente bloccato durante l'esecuzione del suo costruttore, esso è quindi impossibilitato per quanto detto sopra a sollevare o ricevere eventi.
|
2.3. Osservatori
Gli osservatori (
Observers) sono oggetti che permettono d'intercettare eventi lanciati da altri oggetti che sono "osservati" appunto. E' possibile intercettare eventi
prima che siano lanciati o
subito dopo che lo sono stati. Per ogni evento intercettato, l'osservatore sollevarà un'evento che avrà lo stesso nome e lo stesso argomento.
Usando l'istruzione
STOP EVENT all'interno di un' osservatore di eventi l' evento stesso originale verrà cancellato.
Esempio
PRIVATE $hBottone as Button
PRIVATE $hOsservatore AS Observer
PUBLIC SUB Form_Load()
$hBottone = NEW Button(ME) AS "Bottone"
$hOsservatore = NEW Observer(hBottone) AS "MioOsservatore"
END
PUBLIC SUB MioOsservatore_Click()
DEBUG "Il bottone è stato premuto e l'evento cancellato!"
STOP EVENT
END
PUBLIC SUB Bottone_Click()
DEBUG "Questo testo non sarà mai visibile."
END
3. Ereditarietà
3.1. Che cosa è l'ereditarietà?
L'ereditarietà è la capacità di poter
specializzare una classe partendo da una classe padre,
ereditando tutti i metodi,le proprietà e gli eventi.
Così che non si debba duplicare del codice già pronto e funzionante per poter costruire una classe che abbia molte similitudini, ma che al contempo aggiunga nuove funzionalità.
 |
Attenzione! E' necessario usare la parola chiave ME per accedere agli elementi ereditati dall'interno della classe.
|
3.2. Quale classe può essere una classe padre?
È possibile ereditare da qualsiasi classe, anche da una nativa! Non è stupendo?
Per esempio, è possibile creare una MiaListBox personalizzata ereditando dalla classe
ListBox, e aggiungervi per esempio la caratteristica di poter associare un tag (cioè qualcosa di estremamente vario da poter utilizzare in molti modi) uno per ogni elemento della lista.
Notare che non è possibile usare
INHERITS in un file di classe FORM, questo perché esso già eredita dalla classe
Form.
 |
Attenzione! La profondità con cui è possibile creare nuovi oggetti ereditandoli, non può superare i 16 livelli. Questo è il limite che l'interprete di Gambas pone.
|
3.3. Dispacciamento virtuale
Quando un metodo viene chiamato, o si accede a una proprietà tramite un riferimento ad un'oggetto,
Gambas permette di usare il
dispacciamento virtuale (
../../def/virtual dispatching).
Questo significa che vengono usati i dati direttamente della classe è non quelli della variabile che si riferisce all'oggetto, (come accadeva in Gambas 1.0).
3.4. Ereditarietà e costruttori
Contrariamente a tutti i linguaggi a oggetti conosciuti, ogni classe nella gerarchia dell'ereditarietà, utilizza i parametri passati al costruttore.
Supponiamo di avere la seguente struttura ereditata:
MiaListBox ---eredita da--> ListBox ---eredita da---> Control.
- Control._new() non esiste.
- ListBox._new() prende un parametro: il controllo padre.
- MiaListBox._new() prende un parametro: un nome (è giusto un'esempio).
Così MiaListBox= avrà due parametri.
- Il primo sarà inviato da MiaListBox._new().
- Il secondo, da ListBox._new().
Siate precisi: il
ListBox._new()
sarà chiamato per primo, così che vi sarà la certezza che il controllo
ListBox esista quando saremo in MiaListBox.new().
Gli argomenti devono essere specificati in ordine inverso.
Poi si crea un controllo MiaListBox in questo modo:
hMiaListBox = NEW MiaListBox("Nome", hContainer)
In
Gambas 3, il meccanismo con cui vengono processati gli argomenti del costruttore è cambiato:
- Gli argomenti obbligatori vengono processati per primi, successivamente quelli opzionale se questi sono disponibili.
- L'ordine non è più invertito.
Per esempio, seupponiamo di avere il seguente schema di ereditarietà:
MioForm ---eredita da--> Form ---eredita da--> Window
Con il costruttore MioForm sono:
Sub _new(PrimoArgomento As String, SecondoArgomento as Integer, Optional TerzoArgomento As Integer)
Notare: Il costruttore del
Form non accetta argomenti, e il costruttore di
Window prende un'argomento opzionale dal genitore. La sintassi del costruttore finale sarà:
New MioForm(PrimoArgomento As String, SecondoArgomento As Integer, Optional Parent As Control, Optional TerzoArgomento As Integer)
In linea generale, l'ordine degli argomenti per un'ereditarietà a tre livelli è così composto:
- Argomenti obbligatori del costruttore del controllo padre del padre (nonno).
- Argomenti obbligatori del costruttore del controllo padre.
- Argomenti obbligatori del costruttore del controllo finale.
- Argomenti opzionali del costruttore del controllo padre del padre (nonno).
- Argomenti opzionali del costruttore del controllo padre.
- Argomenti opzionali del costruttore del controllo finale.
3.5. Sovrascrittura Overriding
Quando un simbolo viene sovrascritto, il tipo di simbolo della classe figlia, deve essere
compatibile con il tipo della classe padre.
Le regole sono:
- Un simbolo dinamico deve essere sovrascritto da un'altro simbolo dinamico, uno statico da un'altro statico.
- Un metodo deve essere sovrascritto da un metodo.
- Una proprietà deve essere sovrascritta da una proprietà.
- Una proprietà a sola lettura, può essere sovrascritta da una proprietà lettura/scrittura.
- Una costante può essere sovrascritta da una costante o una proprietà.
4. Componenti
In
Gambas i componenti (
components), sono librerie esterne condivise scritte in C, C++ o direttamente in Gambas che aggiungono nuove funzionalità e/o classi all'interprete. Le Classi (
Classes) sono gruppi omegenei di classi raggruppate da cui provengono.
4.1. Componenti interni predefiniti
L'interprete racchiude al suo interno dei componenti chiamati
gb che definiscono tutte le classi predefinite del linguaggio. Questi componenti, vengono caricati in modo predefinito e possono essere quasi considerati parte del linguaggio.
4.2. Tabelle di simboli
Ogni componente possiede una classe con tabella dei simboli privata, in modo che i nomi delle classi non siano in conflitto fra loro.
4.3. Tabella globale dei simboli
Per fare in modo che i componenti possano lavorare insieme, quindi non entrando in conflitto fra loro, esiste una tabella dei simboli globale, in cui sono memorizzati tutte le classi esportate dai componenti e tutte le classi esportate dal progetto corrente.
Se c'è un conflitto di nomi in questa tabella dei simboli globale, l'ultima classe caricata sovrascrive quella precedentemente caricata con lo stesso
nome, utilizzando l'ereditarietà. In altre parole, la classe prevalente sovrascritta, diventa un'estensione della prima.
Questa ultima peculiarità può essere usata per:
- Estendere una classe già dichiarata, aggiungendo nuovi metodi o proprietà o entrambi se necessario. Per esempio, la classe gb.qt Application reimplementa la classe gb Application.
- Sovrascrittura di metodi in una classe già dichiarata. Per esempio, il componente gb.form.dialog sostituisce molti dei metodi statici della classe Dialog.
4.4. Tabella dei simboli di progetto
Il vostro progetto ha il proprio simbolo privato, tipo un componente (
componenti), e può esportare qualsiasi delle sue classi, tramite la tabella dei simboli globale usando la parola chiave
EXPORT.
Le classi del progetto sono caricate
dopo tutti i componenti. Così la vostra classe esportata, può eventualmente sovrascrivere qualsiasi classe dichiarata in un componente.