Creare da codice una Classe specifica

Da Gambas-it.org - Wikipedia.

Oltre alle Classi native di Gambas, è possibile creare da codice Classi specifiche aventi risorse proprie: Proprietà, Metodi ed Eventi. [nota 1][nota 2]

Il progetto della Classe particolare deve essere creato dall'interfaccia IDE di Gambas cliccando con il tasto destro del mouse sull'elemento "Progetto" visibile nella colonna ad albero a sinistra.

Le risorse della Classe specifica

Le risorse specifiche della Classe specifica devono essere definite via codice all'interno della pagina del progetto nell'IDE di Gambas.

Le "Proprietà" della Classe specifica

In via generale la Proprietà di una Classe è una risorsa che determina le caratteristiche di quella Classe.
L'attribuzione di tali caratteristiche ad un'istanza concreta (Oggetto ) della Classe avviene attraverso l'assegnazione con l'operatore "=" di una valore alla variabile che rappresenta la particolare Proprietà.

Le Proprietà di una Classe specifica sono a questa attribuite in fase progettuale mediante la parola-chiave Property. [nota 3]

Leggiamo dalla guida in linea:

In automatico una volta dichiarata una proprietà, l'editor di Gambas crea una funzione per la lettura e una subroutine per la scrittura della proprietà stessa.
Se invece opzionalmente viene utilizzata la parola chiave facoltativa READ, allora la proprietà così creata diventa a sola lettura, e l'editor di Gambas non creerà la subroutine di scrittura.
Una volta dichiarata nella classe specificata, una proprietà deve essere implementata. Si tratta in pratica di scrivere del codice nella funzione che legge la proprietà.
E se la proprietà non è a sola lettura, e cioè la si può anche scrivere, di scrivere del codice anche nella subroutine di scrittura.
Il nome della funzione per la lettura, è il nome della proprietà seguito da un carattere di sottolineatura e dalla parola Read.
Questa funzione così composta non necessita di argomenti e deve restituire un valore il cui tipo sia lo stesso dichiarato nella proprietà.
Il nome della subroutine di scrittura, è il nome della proprietà seguito da un carattere di sottolineatura e dalla parola Write.
Questa subroutine essendo tale non ritorna ovviamente niente, ma prende un argomento (uno solo) che gli viene passato che deve essere dello stesso tipo dichiarato nella proprietà per poterlo poi assegnare alla funzione Read di lettura eventualmente elaborato.

In particolare va considerato che:

Property Nome As Tipo

definisce una Proprietà in lettura/scrittura;

Property Read Nome As Tipo

definisce una Proprietà in sola lettura;

Property Write Nome As Tipo

definisce una Proprietà in sola scrittura.


Alla Proprietà deve essere poi associato un simbolo (una variabile ) dello stesso tipo per contenerne il valore.

Private Simbolo As Tipo

Ad essa, infine, sono associate due funzioni (una sola, se la Proprietà è di sola lettura), per consentire di leggervi un valore assegnato:

Private Function Nome_Read() As Tipo

 Return Simbolo

End

e per assegnare alla Proprietà un valore:

Private Sub Nome_Write(Value)

 Simbolo = Value

End

Esempio pratico

Supponiamo di aver creato da codice una Classe specifica, che chiameremo Automobile: per ciascuna automobile bisognerà gestire diversi parametri:
- Colore
- Cilindrata
- Modello


La Classe Automobile sarà costruita come segue:

' Definisce le Properità:
Property Colore As Integer
Property Modello As String
Property Cilindrata As Short
Property Read Allestimento As String   ' In questo caso è definita anche una "Proprietà" di "sola" lettura

' Definisce i simboli associati alle Proprietà:
Private $Colore As Integer
Private $Modello As String
Private $Cilindrata As Short
Private $Allestimento As String

' Definisce le Funzioni per gestire le suddette Proprietà:

' La Proprietà di "sola" lettura potrà comunque essere valorizzata una sola volta con il metodo nascosto "_new()", passandogli un valore esclusivamente all'atto della creazione dell'istanza (Oggetto) della Classe specifica nella Classe superiore
Public Sub _new(versione As String)

  $Allestimento = versione

End

' Con questa funzione si "legge" dalla Proprietà Colore, in quanto la funzione ritorna appunto il valore contenuto dalla variabile/simbolo associata alla predetta Proprietà:
Private Function Colore_Read() As Integer
  Return $Colore
End

' Con questa funzione si "scrive" nella variabile/simbolo associata alla Proprietà Colore; e pertanto le si assegna un valore:
Private Sub Colore_Write(Value As Integer)  
  $Colore = Value  
End

Private Function Modello_Read() As String  
  Return $Modello  
End  

Private Sub Modello_Write(Value As String)  
  $Modello = Value  
End

Private Function Cilindrata_Read() As Short  
  Return $Cilindrata  
End  

Private Sub Cilindrata_Write(Value As Short)  
  $Cilindrata = Value  
End  

Private Function Allestimento_Read() As String
  Return $Allestimento  
End

Differenza in una Classe specifica fra la Costante e la Proprietà di sola lettura

Come è noto, una Proprietà di "sola lettura" appartenente a una Classe specifica, non consente di modificare il proprio valore di inizializzazione; proprio come fosse una Costante.
Eppure, in una Classe specifica è riscontrabile una differenza fra la "Costante" e la Proprietà di "sola lettura".

La Costante è caratterizzata - potremmo dire - da una immmodificabilità "assoluta" della sua inizializzazione, ossia del valore assegnatole all'atto della creazione della Classe specifica: in qualunque caso e in qualunque modo il valore rappresentato dalla Costante è immodificabile.
Va comunque aggiunto che la Costante è spesso utilizzata nel codice di una Classe specifica mediante il proprio nome identificatore preceduto da un trattino in basso (che la rende invisibile a ogni "istanza" della Classe specifica):

Public Const _IDENTIFICATORE As Integer

non perché in tal caso debba essere richiama e usata da un'istanza (l'Oggetto) della Classe, bensì solo per definire le "Proprietà", il nome del "Gruppo" degli Eventi e gli "Eventi" posseduti dalla Classe medesima.
Esempio:

Public Const _Properties As String = "*,Picture,Mode{Select.*}=Single,Orientation{Arrange.Horizontal;Vertical}=Vertical,Sorted,Editable"
Public Const _Group As String = "View"
Public Const _DefaultEvent As String = "Click"

Risorse queste che in tal modo potranno anche essere visualizzate nell'IDE, se il file della Classe venisse adeguatamente caricato nell'IDE.

La Proprietà di sola lettura, invece, possiede una immodificabilità "relativa", ossia l'impossibilità di modificare dall'esterno del codice della Classe specifica attraverso l'istanza della Classe il valore assegnatole nel momento della istanziazione (creazione dell'Oggetto) della Classe di appartenenza (inizializzazione ). Essa, però, risulta comunque suscettibile di modifica all'interno del codice della Classe specifica di appartenenza e solo all'atto della istanziazione della Classe.

Mostriamo un esempio, nel quale si ha un progetto di creazione di una Classe specifica contenente una Proprietà di "sola lettura", il cui valore verrà più volte modificato:

Property Read solalettura As Integer

Private $solalettura As Integer = 1000   ' Inizializzazione interna

' Inizializzazione dall'esterno, ma "solo nel momento della istanziazione" di questa Classe specifica
Public Sub _new(i As Integer)
 
 $solalettura = $solalettura + i      ' Il valore iniziale può comunque essere modificato

End


Private Function solalettura_Read() As Integer

 $solalettura = $solalettura + 1000   ' Ulteriore modifica del valore della proprietà

 Return $solalettura

End


I "Metodi" della Classe specifica

In via generale il Metodo di una Classe è una risorsa che determina la capacità della Classe di compiere un'azione (fare qualcosa ), e più precismente lo svolgimento di un'operazione.
L'azione/operazione viene compiuta dal Metodo su uno o più valori che - solitamente - gli vengono passati come argomenti nelle parentesi operative.

I Metodi di una Classe specifica sono a questa attribuiti in fase progettuale mediante una delle seguenti parole-chiave: Function (se il Metodo ritorna comunque un valore), oppure Procedure oppure Sub.

In ogni caso il Metodo va dichiarato preceduto dalla parola-chiave Public.

Esempio pratico

Nel progetto della Classe specifica andrà inserito una routine di Funzione/Procedura, come l'esempio astratto che segue:

Public Function(Parametro_1 As Tipo_di_dati, Parametro_2 As Tipo_di_dati, ..., Parametro_N As Tipo_di_dati) As Tipo_di_Dati

  valore = ...operazione...

  Return valore

End

Nel caso che il Metodo non debba ritornare alcun valore, ma eseguire altri compiti:

Public Procedure(Parametro_1 As Tipo_di_dati, Parametro_2 As Tipo_di_dati, ..., Parametro_N As Tipo_di_dati)

  valore = ...fa qualcosa...

End

Ovviamente la Funzione (e anche ovviamente la Procedura e la Sub ) può contenere tante righe di istruzione, quante sono necessarie per ottenere il risultato previsto.


Gli "Eventi" della Classe specifica

In via generale l'Evento di una Classe è una risorsa che si esplica, quando accade qualcosa previsto nel codice, o - meglio - quando si verifica una situazione/consizione prevista dal codice.

Gli Eventi di una Classe specifica sono a questa attribuiti in fase progettuale mediante la parola-chiave: Event.

La parola chiave EVENT, seguita dal nome identificativo dell'Evento, istruisce il compilatore che il nome indicato corrisponde ad un Evento, ovvero, permette di implementare tale Evento negli Oggetti, istanze della Classe specifica, e le relative modalità di intercettazione:

EVENT NomeIdentificativo

Se l'Evento deve passare dei dati, allora il nome identificativo dell'Evento sarà seguito da uno o più parametri all'interno di due parentesi tonde:

EVENT NomeIdentificativo(Parametro_1 As Tipo_di_Dati, Parametro_2 As Tipo_di_Dati, ..., Parametro_N As Tipo_di_Dati)

Sollevare l'Evento

Per far sollevare l'Evento bisognerà inserire nella routine, ove si verificheranno le condizioni per lo scatenarsi di Evento, l'istruzione RAISE seguita dal nome identificativo dell'Evento.

RAISE NomeIdentificativo


Tale routine ovviamente è posta nel progetto della Classe specifica.

Qualora la dichiarazione del nome identificativo dell'Evento comporti anche il passaggio di argomenti, ugualmente l'istruzione Raise dovrà esere seguita dal nome identificativo dell'Evento, da sollevare, seguito da uno o più argomenti all'interno di due parentesi tonde:

 RAISE NomeIdentificativo(Argomento)

Ovviamente sarà necessario che tutti i metodi "risponditori" a questo Evento, posti nella Classe principale, prevedano l'intercettazione di quell'argomento passato dall'Evento medesimo:

 Public Sub NomeIdentificativo(variabile As Tipo_di_Dati)
 ...

In questa situazione, oltre allo scatenare un evento, abbiamo la possibilità di fornire ulteriori informazioni ai metodi "risponditori", e questo, ovviamente, amplia notevolmente le possibilità nelle nostre applicazioni.
E' comunque consigliato il fare attenzione a non esagerare nel numero di parametri passati tramite gli eventi, perchè potrebbe causare una riduzione delle risorse e della velocità di esecuzione.


Esempio pratico

Nella Classe principale viene chiamata una routine presente nella Classe secondaria (che chiameremo "Cprova.class") che scatenerà l'evento previsto nella Classe principale.
La parola-chiave "Event", posta nella Classe secondaria, scatena un Evento previsto e intercettato nella Classe principale.

' Qui siamo nella classe principale FMain.Class

' Dichiariamo una variabile del tipo della classe secondaria CProva:
Private prova As Cprova


Public Sub Form_Open()

' Creiamo la variabile "evProva" del tipo della classe secondaria CProva:
 prova = New Cprova As "evProva"

' Viene chiamata la sub-routine nella classe secondaria:
 prova.funzSecond()

End


' Se sollevato l'Evento "evento" nella classe secondaria, viene scatenata questa routine, alla quale viene passata una stringa:
Public Sub evProva_evento(testo As String)

' Il testo, contenuto nella variabile stringa, passata dalla classe secondaria, viene mostrato in console:
 Print testo

End

Quando viene chiamata la sub-routine nella classe secondaria Cprova.class, questa solleva l'Evento nella Classe Principale:

' Questa invece è la classe secondaria "CProva.class"

' Con Event viene dichiarato l'Evento (al quale in questo esempio è dato il nome “evento”) ed un parametro di tipo "Stringa".
' Tale parametro rappresenta il tipo di valore che sarà passato alla routine della Classe "principale"; routine che sarà invocata dalla sollevazione dell'evento medesimo:
 Event evento(txt As String)

Public Sub funzSecond()

 Dim s As String
 
 s = "E' stato sollevato l'Evento !"

' Con Raise viene sollevato l'Evento “evento”, il quale scatenerà la sub-routine "evProva_evento(testo As String)" presente nella classe principale, e le passa il valore della variabile “s”:
 Raise evento(s)

End


L'uso della parola Event può avvenire soltanto all'interno di una Classe, non essendo possibile sollevare un evento in un Modulo.

Inoltre, la parola Event può essere presente all'interno della Classe principale e sollevare un evento previsto nella Classe principale medesima.
Esempio:

Event evento(testo As String)


Public Sub Form_Open()

 Raise evento("Evento sollevato !")
 
End


Public Sub Form_evento(s As String)
 
 Print s
 
End


Passare una Struttura con Event

Riguardo a tale argomento si rinvia alla lettura della seguente pagina: Passare una Struttura con Event


Interruzione di un Evento

Per interrompere, o comunque impedire la sollevazione di un determinato evento, è possibile utilizzare il comando Stop innanzi alla parola chiave Event.

Esempio di interruzione dell'evento chiusura del Form:

Public Sub Form_Close()

  If Message.Question("Chiudo?","Si","No") = 2 Then Stop Event

End

Come è possibile notare, il comando Stop non disabilita né blocca il Controllo, semplicemente interrompe la sollevazione del previsto Evento.


Classe con dichiarazione di sole semplici variabili pubbliche

In una Classe specifica, creata da codice, anziché prevedere e impostare le Proprietà, è possibile dichiarare semplici variabili, purché esse sia Globali e Publiche.
Tali variabili saranno richiamate nella Classe principale nel modo consueto, ossia dalla variabile del tipo della Classe specifica.

Leggiamo, infatti, dalla guida in linea relativa alla parola Property:

E' possibile evitare l'utilizzo delle proprietà con delle variabili pubbliche dichiarate all'interno della Classe.
Tutto ciò se pur più semplice ed ammesso e meno complesso presenta lo svantaggio che in qualsiasi parte del programma sia possibile modificare i valori senza nessun controllo,
o meglio il controllo deve essere fatto prima infrangendo il meccanismo dell'incasplulamento.
Se questo per piccoli programmi è accettabile non lo è per programmi più corposi. Infatti implementando le proprietà, è possibile controllare che la coerenza dei dati sia tale
in tutte le situazioni al di là della consistenza del programma.

In modo analogo è possibile dichiarare e impostare delle Costanti, da usare nella Classe specifica con le consuete modalità.

Esempio pratico

Nel progetto della Classe specifica sarà sufficiente dichiarare una o più variabili Globali e Publiche:

Public Bvariabile As Byte

Public Ivariabile As Integer

Public $variabile As String

Public Const COSTANTE As Short

e così via.

Parimenti è possibile dichiarare e impostare delle Costanti, da usare nella Classe specifica con le consuete modalità.


Utilizzare nella Classe specifica creata le risorse di un'altra Classe

Per utilizzare, senza doverle dichiarare nuovamente, le risorse (Proprietà, Metodi, Eventi, Costanti e Variabili ) appartenenti ad un'altra Classe, si farà uso del concetto di Ereditarietà.

La Classe, della quale si vogliono sfruttare le risorse possedute è la Classe Padre; mentre la Classe che utilizzerà, ereditandole appunto, quelle risorse della Classe Padre, è la Classe Figlia. [nota 1]

Per ottenere l'Ereditarietà delle risorse di una Classe Padre, si dovrà scrivere nella Classe Figlia come sua prima istruzione la parola-chiave INHERITS:

Inherits nome_Classe_Padre_da_cui_ereditare


Installare l'icona di una Classe specifica di Controllo grafico da noi creata

E' possibile far apparire e utilizzare l'icona di un Controllo grafico, da noi creato, nella finestra dei widget disponibili, purché si proceda come segue:

  • creare un'applicazione grafica;
  • eliminare nella banda di sinistra il Form principale (Fmain.Class);
  • creare nella banda di sinistra un nuovo Form;
  • creare la nuova Classe specifica, avendo cura di assegnarle un nome con tutte le lettere in minuscolo e di inserire all'inizio del suo codice interno le istruzioni:
Export
Inherits UserControl
  • creare nella Cartella nascosta ".hidden", facente parte della Cartella principale dell'applicazione, una nuova Cartella chiamata "control" (tutte lettere in minuscolo;
  • inserire all'interno della predetta sub-cartella "control" il file dell'icona prescelta, possibilmente di dimensioni 128x128 pixel, per rappresentare il Controllo nella finestra dei widget disponibili, da noi creato;
  • l'icona dovrà essere un file immagine di formato PNG e dovrà avere il medesimo nome (a lettere minuscole) della Classe specifica, da noi creata, che rappresenterà).



Note

[1] Vedere anche la seguente pagina della Wiki: Oggetti, Classi e Moduli.

[2] Una Classe specifica, appositamente creata, può ben sostituire una Struttura, in quanto si riserva una quantità di memoria determinata dal tipo di variabili da esse formata.

[3] Seguiremo in questo paragrafo, per quello che riguarda l'uso della parola-chiave Property, in buona parte quanto descritto in analoga discussione nel forum di Gambas-it.org dall'utente sotema.