Le modèle objet de Gambas
1. Les objets & classes
Un
objet en Gambas est une structure de données qui fournit des propriétés, des variables, des méthodes et des évènements.
L'accès aux objets se fait "par référence", c.a.d. en utilisant un pointeur, ou en utilisant une variable dont la valeur est l'adresse mémoire de la structure de données de l'objet.
Vous pouvez voir l'adresse d'un objet en utilisant l'instruction
PRINT :
DIM aStr AS NEW String[]
PRINT aStr
(String[] 0x80dccf8)
La structure de données de l'objet est décrite par une
classe.
1.1. Les classes
Donc chaque objet Gambas a une
classe qui décrit toutes ses propriétés, méthodes et évènements publics.
Cette classe est aussi un objet Gambas, dont la classe est la classe nommée
Class.
Une
classe statique est une classe dont tous les membres sont
statiques (voir ci-dessous). En Gambas, une classe statique est aussi nommée
module.
Une classe statique ne peut pas être instanciée : elle créerait un objet sans variable dynamique, ce qui est inutile.
Exemple
La classe
System est une classe statique : toutes ses méthodes sont statiques, et vous ne pouvez pas créer un objet dont la classe serait System.
Une classe
virtual est une pseudo-classe cachée que vous ne pouvez pas manipuler explicitement.
1.2. Les propriétés, méthodes et variables.
Les propriétés, les méthodes permettent de manipuler les structures de données.
Une
Propriété, une
Méthode ou une
Variable peut être
statique:
-
Une variable statique sera partagée par toutes les instances de la même classe.
-
Une propriété ou méthode statique ne peut modifier que les variables statiques.
Une méthode ou une variable peut être soit
publique soit
privée. Une propriété est toujours publique.
Les symboles privés ne peuvent être utilisés que depuis l'intérieur d'une classe.
Les symboles publics peuvent être utilisés partout, à condition que vous ayez une référence pointant sur l'objet.
1.3. Références
Il n'y a pas de garbage collector en Gambas. Aussi chaque objet a un
compteur de références qui est incrémenté chaque fois que l'objet est référencé par n'importe quelle variable, tableau, collection ou autre objet, et décrémenté quand il est libéré.
Ce compteur de références est mis à zéro lors de la création de l'objet, et quand il revient à zéro après la libération d’une référence, l'objet est libéré.
1.4. Les objets invalides
Un objet peut devenir
invalide, lorsque, par exemple, il est relié à un objet interne non géré par Gambas et qui a été détruit.
Essayer d'utiliser un objet invalide provoque une erreur.
1.5. Les méthodes spéciales
Les méthodes spéciales sont des méthodes déclarées dans les classes, dont le nom commence avec un caractère
_
, et qui est appelée par l'interpréteur dans les situations suivantes :
-
Quand un objet est créé.
-
Quand un objet est libéré.
-
Quand la classe de l'objet est chargée.
-
Quand la classe de l'objet est déchargée.
-
En utilisant l'objet comme si c’était un tableau.
-
En énumérant l'objet.
-
En énumérant un objet comme si c’était une fonction.
-
En essayant d'utiliser une méthode ou une propriété inconnue.
Voir
/wiki/api/cat/special pour plus d'informations.
2. Événements & Observateurs
2.1. Les événements
Les événements sont des signaux envoyés par un objet quand quelque chose se passe sur lui.
Si un objet lève des événements, il tiendra une référence sur son
observateur par défaut, ou son
objet parent.
Cet observateur par défaut est un autre objet qui implémente des
gestionnaires d'événements. Un
gestionnaire d'événement est juste une méthode publique appelée à chaque fois que l’événement survient.
Par défaut, l'observateur est l'objet courant où le nouvel objet instancié est déclaré.
Pour générer les événements, un objet doit avoir un
nom d'événement. Ce nom d'événement est assigné à l'instanciation de l'objet, quand l'instruction
NEW et le mot-clé
AS sont utilisés. Il est le préfixe de toutes les gestionnaires d'événement.
Exemple
Ceci crée un contrôle
Button qui lèvera des événements.
DIM hButton AS Button
hButton = NEW Button(ME) AS "ButtonEventName"
Public Sub ButtonEventName_Click()
Print "This is the 'Click' event handler for the previous button."
End
Si aucun nom d'événement n'est indiqué, alors l'objet ne lèvera aucun 'événement.
2.2. Objet parent par défaut (ou observateur par défaut)
Certains objets peuvent avoir un objet parent par défaut si vous ne le définissez pas en spécifiant un nom d'évènement avec la syntaxe
As "xxx"
.
Pour le moment, les seuls objets ayant un objet parent par défaut sont les
formulaires, qui s'utilisent eux-mêmes comme objet parent par défaut.
Ceci est conçu pour mimer le comportement de Visual Basic&trade ; et aider les anciens développeurs VB.
Si vous créez un Formulaire et spécifiez son nom d'évènement avec la syntaxe
As "xxx"
, ce comportement par défaut sera surchargé.
2.3. Verrouillage des objets.
Un objet peut être verrouillé de telle sorte qu'il suspende l’émission de ses événements, et peut être débloqué afin qu'il les lève de nouveau.
Reportez-vous aux méthodes
Object.Lock et
Object.Unlock.
Certains événements peuvent être annulés par le gestionnaire d'événement, en utilisant l'instruction
STOP EVENT.
L'effet de cette annulation dépend de l’événement.
Un objet est automatiquement verrouillé pendant l’exécution de son constructeur : il ne peut ni envoyer, ni recevoir d’événement.
2.4. Les observateurs
Les
observateurs sont des objets qui vous permettent d’intercepter les événements levés par d’autres objets. Ils les "observent".
Vous pouvez intercepter les événements juste avant qu’ils soient levés, ou juste après.
Pour chaque événement intercepté, l’observateur provoquera un événement du même nom avec les mêmes arguments. Ces évènements sont levés en plus de l'évènement originel géré par l'
objet parent.
Si vous utilisez
STOP EVENT dans un observateur gestionnaire d’évènement, l’évènement originel est annulé.
Exemple
PRIVATE $hButton as Button
PRIVATE $hObserver AS Observer
PUBLIC SUB Form_Load()
$hButton = NEW Button(ME) AS "Button"
$hObserver = NEW Observer(hButton) AS "Observer"
END
PUBLIC SUB Observer_Click()
DEBUG "Le bouton a été cliqué. J’annule l’évènement !"
STOP EVENT
END
PUBLIC SUB Button_Click()
DEBUG "Vous ne devriez pas me voir."
3. Héritage
L'héritage est une manière pour une classe de devenir une version spécialisée d'une autre classe.
3.1. Qu'est-ce qui est hérité ?
La classe hérite de tous les méthodes, propriétés, constantes et évènements de son parent.
Vous devez utiliser le mot-clé
ME pour accéder aux éléments hérités depuis l'intérieur de la classe.
3.2. Quelles classes peuvent être une classe parent ?
Vous pouvez hériter de n'importe quelle classe, y compris une classe native.
Par exemple, vous pouvez créer une classe personnalisée MaListBox qui hérite de
ListBox, mais qui permet d'associer une étiquette (un tag) à chaque objet de la liste.
Notez que vous ne pouvez pas utiliser
INHERITS
dans un fichier de classe de formulaire, parce que les formulaires héritent déjà de la classe
Form.
La profondeur de l'arbre d'héritage ne peut pas être plus supérieure à 16. C'est une constante codée en dur dans l'interpréteur Gambas.
3.3. Le dispatching virtuel
Lorsqu'on appelle une méthode ou lorsqu'on accède à une propriété d'une référence d'un objet, Gambas utilise toujours le
dispatching virtuel.
Cela signifie que la classe réelle de l'objet est toujours utilisée, et non pas le type de la variable qui référence l'objet - Comme c'était malencontreusement le cas pour Gambas 1.0.
3.4. Héritage et constructeur
Attention ! Contrairement à tous les langages objets que je connais, chaque classe dans la hiérarchie d'héritage consomme les paramètres passés au constructeur.
Supposons que nous ayons l'arbre d'héritage suivant :
MaListeBox ---hérite de--> ListBox ---hérite de---> Control
-
Control._new() n'existe pas.
-
ListBox._new() prend un paramètre : le contrôle parent.
-
MyListBox._new() prend un paramètre : un nom - C'est juste un exemple.
Donc
NEW MaListeBox
prendra deux paramètres.
-
Le premier sera envoyé à ListBox._new().
-
Le second à MaListeBox._new().
Attention : le
ListBox._new() sera appelé en premier, afin d'assurer que le contrôle
ListBox existe quand vous êtes dans MaListeBox._new().
Ensuite vous créez un contrôle MaListeBox de cette manière :
hMaListeBox = NEW MaListeBox(hContainer, "Nom")
De sorte que l'ordre des arguments est le suivant :
-
Les arguments obligatoires sont consommés en premier, puis les arguments optionnels s’ils sont disponibles.
-
les arguments des plus anciennes classes sont spécifiés en premier !
En Gambas 2.0, l'ordre des arguments est inversé !
Par exemple, si vous avez l’héritage suivant :
MyForm --> Form --> Window
Avec le constructeur de MyForm suivant :
Sub _new(FirstArg As String, SecondArg as Integer, Optional ThirdArg As Integer)
Note : le constructeur de
Form ne prend pas d’argument, et le constructeur de
Window prend un argument parent optionnel.
La signature du constructeur final sera :
New MyForm(FirstArg As String, SecondArg As Integer, Optional Parent As Control, Optional ThirdArg As Integer)
D’une manière plus générale, l’ordre des arguments pour un héritage à trois niveaux est :
-
Arguments obligatoires du constructeur grand-parent.
-
Arguments obligatoires du constructeur parent.
-
arguments obligatoires du constructeur final.
-
Arguments optionnels du constructeur grand-parent.
-
Arguments optionnels du constructeur parent.
-
Arguments optionnels du constructeur final.
3.5. Surcharge des symboles
Quand un symbole est surchargé, la signature du symbole dans la classe fille doit être la même que celle du symbole dans la classe parent.
Les règles sont :
-
Un symbole dynamique doit être surchargé par un symbole dynamique, un symbole statique par un symbole statique.
-
Une méthode doit être surchargée par une méthode avec exactement la même signature (mêmes types de donnée des arguments, même type de donnée de la valeur retournée, s'il y en a une).
-
Une propriété en lecture/écriture doit être surchargée par une propriété en lecture/écriture du même type de donnée.
-
Une propriété en lecture seule doit être surchargée par une propriété en lecture seule avec le même type de donnée.
-
Une constante peut être surchargée par une constante du même type de donnée.
4. Les composants
Les composants Gambas sont des bibliothèques partagées externes écrites en C/C++ ou en Gambas, qui ajoutent de nouvelles fonctions ou de nouvelles classes à l'interpréteur.
Les
Classes sont regroupées selon le composant d'où elles proviennent.
4.1. Le composant interne par défaut
L'interpréteur inclut un composant interne nommé
gb qui définit toutes les classes standard du langage.
Ce composant est toujours chargé par défaut, et peut être considéré comme faisant partie du langage.
4.2. Les tables de symboles
Chaque composant a sa propre table de symboles de classes, de sorte que les noms de classes n’entrent pas en conflit.
4.3. La table de symbole globale
Pour que les composants puissent fonctionner ensemble, il y a une table de symboles globale, où toutes les classes exportées par les composants et les classes exportées par le projet actuel sont rangées.
S’il y a un conflit de nom dans cette table de symbole globale, la dernière classe chargée surcharge la classe du même nom chargée précédemment, en utilisant l'héritage. En d’autres termes, la classe ‘surchargeante’ étend celle qui est surchargée.
Cette dernière caractéristique peut être utilisée pour :
-
Etendre une classe déjà déclarée en ajoutant de nouvelles méthodes ou propriétés à cette dernière. Par exemple, la classe Application
gb.qt4
ré-implémente la classe Application gb
.
-
Surcharger les méthodes d'une classe déjà déclarée. Par exemple, le composant
gb.form.dialog
remplace la plupart des méthodes statique de la classe Dialog.
4.4. La table de symboles du projet
Votre projet a son propre symbole privé, comme n'importe quel composant, et peut exporter vers la table de symboles globale n'importe laquelle de ses classes en utilisant le mot-clé
EXPORT,
Les classes du projet sont chargées après l'ensemble des composants. Ainsi vos classes exportées peuvent surcharger des classes exportées déclarées dans un composant.
Voir aussi