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.
Les objets sont accédés "par référence", c.a.d. en utilisant un pointeur, c.a.d. 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 virtuelle 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 et les variables 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. Donc 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
/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, ou son
objet parent.
Cet observateur 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"
Si aucun nom d'événement n'est indiqué, alors l'
objet ne lèvera aucun d'événements.
2.2. 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.3. 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.
En utilisant
STOP EVENT dans un observateur gestionnaire d’événement, vous pouvez annuler l’événement originel.
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."
END
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 toutes 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 MyListBox 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 de l'héritage ne peut pas être plus grande que 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érencie l'objet - Comme c'était malencontreusement le cas en
Gambas 1.0.
3.4. Héritage et constructeur
Attention ! Contrairement à tous les langages objet 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é à MaListeBox._new().
- Le second à ListBox._new().
Soyez prudent : le
ListBox._new() sera appelé en premier, de telle sorte que vous soyez sur que le contrôle
ListBox existe quand vous êtes dans MaListeBox._new().
Ainsi les arguments doivent être indiqués dans l'ordre inverse.
Ensuite vous créez un contrôle MaListeBox de cette manière:
hMaListeBox = NEW MaListeBox("Nom", hContainer)
3.5. Surcharge des symboles
Quand un symbole est surchargé, la signature du symbole dans la
classe fille doit être la même signature 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.
 |
Gambas 2 ne vérifie pas la compatibilité de la surcharge de symboles, faites attention!
|
4. Les composants
Les
composants Gambas sont des bibliothèques partagées écrites en C/C++, ou bien encore des exécutables
Gambas, qui ajoutent des 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 telle 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 symbole 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é 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.qt 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 symbole du projet
Votre projet a ses propres symboles privés, comme n'importe quel
composant, en utilisant le mot-clé
EXPORT, il peut exporter vers la table de symbole globale n'importe laquelle de ses classes.
Les classes du projet sont chargées après tout les composants. Ainsi vos
classe exportées peuvent surcharger des classes exportées déclarées dans un composant.