Chapitre 10 - Les Objets
Contenu
1. Historique de la traduction
2. Présentation
3. Création d'objets
4. Clonage des objets
5. L'accés aux objets
6. Fonctions et object (méthodes)
7. Prototype d'objets
8. Référence à self
9. Encapsulation
10. Réflectivité
1. Historique de la traduction
4 avril 2005 17:52
|
1.0.0
|
Traduction initiale
|
Philippe Le Goff
|
lp--legoff--free--fr
|
2. Présentation
Les objets permettent de regrouper un ensemble de variables et leurs valeurs dans
un contexte commun.
Un objet peut inclure des valeurs scalaires, des séries,
des fonctions ou d'autres objets.
Les objets sont pratiques pour travailler avec des structures complexes car ils
permettent d'encapsuler des variables et le code qui leur est associé, et de les passer simplement à des fonctions.
3. Création d'objets
De nouveaux objets sont construits avec la fonction make.
La fonction make requiert deux arguments et retourne un nouvel objet.
Le format de la fonction make est le suivant :
new-object: make parent-object new-values
Le premier argument, parent-object, est l'objet "parent" à partir duquel le
nouvel objet sera construit. Si aucun objet parent n'est valable, par exemple losqu'on définit
pour la première fois un objet , on utilise par défaut le type object! :
new-object: make object! new-values
Le second argument, new-values, est un bloc contenant les définitions des variables et leurs valeurs initiales pour
ce nouvel objet.
Chaque variable définie au sein de ce bloc est une variable de l'instance de l'objet.
Par exemple, si le bloc contenait deux définitions de variables :
mon-exemple: make object! [
var1: 10
var2: 20
]
L'objet mon-exemple posséde ici deux variables de type integer! (valeurs entières).
Le bloc définissant les variables est évalué, il peut inclure n'importe quelle expression
pour calculer les valeurs des variables :
mon-exemple: make object! [
var1: 10
var2: var1 + 10
var3: now/time
]
Une fois que l'objet a été créé, il peut servir de prototype
pour créer d'autre objets:
mon-exemple2: make mon-exemple []
L'exemple ci-dessus crée une seconde instance de l'objet mon-exemple.
De nouvelles valeurs peuvent être mises dans le bloc :
mon-exemple2: make mon-exemple [
var1: 30
var2: var1 + 10
]
Ci-dessus, l'objet mon-exemple2 posséde des valeurs différentes de
l'objet mon-exemple original.
L'objet mon-exemple2 peut aussi étendre la définition de l'objet
prototype mon-exemple en lui ajoutant de nouvelles variables :
mon-exemple2: make mon-exemple [
var4: now/date
var5: "example"
]
Le résultat est un objet qui a cinq variables :
trois proviennent de l'objet original, deux sont nouvelles.
Le processus d'extension de la définition d'un objet peut être ainsi répété de nombreuses fois.
Il est aussi possible de créer un objet qui contient des variables initialisées à une valeur quelconque.
Par exemple, en utilisant une initialisation en cascade :
mon-example3: make object! [
var1: var2: var3: var4: none
]
Dans le code précédent, les quatres variables var1 à var4 sont
mises à "none" au sein de l'objet.
Pour résumer, le processus de création d'un objet passe par les étapes suivantes :
- Usage de la fonction make pour créer un nouvel objet basé sur un prototype (un objet parent) ou sur le type object!.
- Ajout au nouvel objet des variables définies dans le bloc .
- Evaluation du bloc, ce qui entraîne l'affectation des variables définies dans le bloc,
avec leurs valeurs pour le nouvel objet.
- Le nouvel objet est retourné comme résultat.
4. Clonage des objets
Quand un objet parent est utilisé pour créer un nouvel objet,
l'objet parent est cloné plutôt que "hérité".
Ceci signifie que si l'objet parent est modifié, il n'y a pas d'effet sur l'objet fils.
Pour illustrer ceci, le code suivant montre la création d'un compte bancaire avec l'objet
bank-account, pour lequel les variables sont mises à "none" :
bank-account: make object! [
first-name:
last-name:
account:
solde: none
]
Pour utiliser le nouvel objet, des valeurs sont fournies
lors de la création d'un compte client luke :
luke: make bank-account [
first-name: "Luke"
last-name: "Lakeswimmer"
account: 89431
solde: $1204.52
]
Puisque les nouveaux comptes sont initiés sur l'objet bank-account ,
il est pratique d'employer une fonction et quelques variables globales
pour les créer.
last-account: 89431
bank-bonus: $10.00
make-account: func [
"Returns a new account object"
f-name [string!] "First name"
l-name [string!] "Last name"
start-solde [money!] "Starting solde"
][
last-account: last-account + 1
make bank-account [
first-name: f-name
last-name: l-name
account: last-account
solde: start-solde + bank-bonus
]
]
A présent, la création d'un nouveau compte pour le client Fred
se réduira seulement à la ligne suivante :
fred: make-account "Fred" "Smith" $500.00
5. L'accés aux objets
Les variables à l'intérieur des objets peuvent être atteintes avec les paths.
Un path est composé du nom de l'objet suivi par le nom de la variable à atteindre.
Ainsi, le code suivant permet d'atteindre les variables dans l'objet mon-exemple :
example/var1
example/var2
Quelques illustrations avec l'objet "compte bancaire" :
print luke/last-name
Lakeswimmer
print fred/solde
$510.00
Avec un path, les variables d'un objet peuvent aussi être modifiées :
fred/solde: $1000.00
print fred/solde
$1000.00
Vous pouvez utiliser aussi la fonction in pour accéder à des variables d'objet en
récupérant leurs mots (words) depuis le contexte de l'objet :
print in fred 'solde
solde
Le mot (word) 'solde fait partie du contexte de l'objet Fred.
Il est possible de connaître la valeur de solde dans le contexte de l'objet Fred,
en utilisant la fonction get :
print get in fred 'solde
$1000.00
Le deuxième argument de la fonction in est un mot littéral (literal word).
Ceci vous permet de changer dynamiquement les mots selon vos besoins :
words: [first-name last-name solde]
foreach word words [print get in fred word]
FredSmith
$1000.00
Chaque mot dans le bloc est utilisé dans la boucle foreach pour obtenir sa valeur dans l'objet.
La fonction in peut aussi servir pour attribuer des valeurs aux variables d'un objet,
en conjuguaison avec la fonction set .
set in fred 'solde $20.00
print fred/solde
$20.00
Si un mot n'est pas défini au sein d'un objet, la fonction in renvoie la valeur none.
Ceci peut être mis à profit pour déterminer si une variable existe ou non dans un objet.
if get in fred 'bank [print fred/bank]
6. Fonctions et object (méthodes)
Un objet peut contenir des variables faisant référence à des fonctions dans l'objet.
Ce peut être utile, car les fonctions sont encapsulées dans le contexte de l'objet, et peuvent
accéder à d'autres variables dans l'objet directement, sans passer par l'usage d'un path.
En guise d'exemple, l'objet mon-autre-exemple va inclure des fonctions qui vont calculer
de nouvelles valeurs au sein de l'objet :
mon-autre-exemple: make object! [
var1: 10
var2: var1 + 10
var3: now/time
set-time: does [var3: now/time]
calculate: func [value] [
var1: value
var2: value + 10
]
]
Remarquez que les fonctions peuvent se référer aux variables de l'objet directement,
sans utiliser de paths.
Ceci est possible car les fonctions sont définies dans le même contexte que les variables auquelles
elles accédent.
Pour définir un nouvel horaire pour la variable var3 :
mon-autre-exemple/set-time
Cet exemple évalue la fonction qui va attribue à la variable var3
l'heure courante.
Pour calculer de nouvelles valeurs pour var1 et var2 :
mon-autre-exemple/calculate 100
print example/var2
110
Dans le cas de l'objet compte bancaire, les fonctions pour un dépôt et un retrait peut être
ajoutées à la définition courante :
bank-account: make bank-account [
depot: func [amount [money!]] [
solde: solde + amount
]
retrait: func [amount [money!]] [
either negative? solde [
print ["Denied. Account overdrawn by"
absolute solde]
][solde: solde - amount]
]
]
Ici, les fonctions se référent à la variable solde directement au sein
du contexte de l'objet.
Ceci parce qu'elles font elles-mêmes partie de ce contexte.
A présent, si un nouveau compte est créé, il contiendra les fonctions
pour le dépôt et le retrait d'argent.
Par exemple :
lily: make-account "Lily" "Lakeswimmer" $1000
print lily/solde
$1010.00
lily/depot $100
print lily/solde
$1110.00
lily/retrait $2000
print lily/solde
-$890.00
lily/retrait $2.10
Denied. Account overdrawn by $890.00
7. Prototype d'objets
N'importe quel objet peut servir de prototype pour créer
de nouveaux objets.
Le compte bancaire lily créé précédemment peut être utilisé
pour construire de nouveaux objets :
maya: make lily []
Ceci définit une instance de l'objet.
L'objet est une copie de l'objet lily et possède des valeurs identiques :
print lily/solde
-$890.00
print maya/solde
-$890.00
Vous pouvez modifier les nouveaux objets en fournissant de nouvelles valeurs
à l'intérieur du bloc qui les définit :
maya: make lily [
first-name: "Maya"
solde: $10000
]
print maya/solde
$10000.00
maya/depot $500
print maya/solde
$10500.00
print maya/first-name
Maya
L'objet lily sert de prototype pour créer le nouvel objet maya.
Remarque:
un mot qui n'a pas été redéfini pour le nouvel objet
continue d'avoir les valeurs de l'ancien objet :
print maya/last-name
Lakeswimmer
De nouveaux mots peuvent être ajoutés à l'objet :
maya: make lily [
email: maya@example.com
birthdate: 4-July-1977
]
8. Référence à self
Chaque objet inclut une variable pré-définie appelée self.
A l'intérieur du contexte de l'objet, la variable self fait référence à l'objet lui-même.
Cette variable peut être utilisée pour passer à l'objet d'autres fonctions
ou pour le retourner en tant que résultat
d'une fonction.
Dans l'exemple suivant, la fonction show-date nécessite un objet en argument et
c'est self qui lui est passé pour cela :
show-date: func [obj] [print obj/date]
example: make object! [
date: now
show: does [show-date self]
]
example/show
16-Jul-2000/11:08:37-7:00
Un autre exemple d'utilisation de la variable self est ici la fonction
new pour le clonage de l'objet :
person: make object! [
name: days-old: none
new: func [name' birthday] [
make self [
name: name'
days-old: now/date - birthday
]
]
]
lulu: person/new "Lulu Ulu" 17-May-1980
print lulu/days-old
7366
9. Encapsulation
L'usage des objets est un bon moyen d'encapsuler un ensemble de variables
qui ne devrait pas apparaître dans le contexte global.
Quand des variables d'une fonction sont définies comme globales, elles peuvent involontairement
être modifiées par d'autres fonctions.
La solution à ce problème de variables globales est de les encapsuler dans un objet.
Ainsi, une fonction peut encore accéder à ses variables, mais celles-ci ne peuvent plus être accédées
depuis le contexte global.
Par exemple :
bank: make object! [
last-account: 89431
bank-bonus: $10.00
set <i>make-account</i> func [
"Returns a new account object"
f-name [string!] "First name"
l-name [string!] "Last name"
start-solde [money!] "Solde Initial"
][
last-account: last-account + 1
make bank-account [
first-name: f-name
last-name: l-name
account: last-account
solde: start-solde + bank-bonus
]
]
]
Ici, les variables sont protégées de modifications
accidentielles, car encapsulées dans le contexte de l'objet bank.
Remarque :
La fonction make-account a été définie en utilisant
la fonction set, plutôt que par une affectation normale.
Avec l'usage de set, la fonction make-account devient une fonction du contexte global.
Cependant, si elle peut être utilisée de la même manière que d'autres fonctions, elle ne nécessite
pas l'usage de paths.
bob: make-account "Bob" "Baker" $4000
10. Réflectivité
Comme beaucoup d'autres types de données REBOL, vous pouvez accéder
aux composants des objets de tel sorte qu'il devient possible d'écrire des outils utiles pour les créer,
les monitorer, ou les débugger.
Les fonctions first et second vous permettent d'accéder aux composants d'un objet.
La fonction first renvoie les mots définis pour un objet.
La fonction second renvoie les valeurs de ces mots.
Le diagramme suivant montre les liens entre les valeurs retournées par les
fonctions first et second.
L'intéret de first est qu'elle permet d'obtenir la liste des mots de l'objet sans
connaitre quoi que ce soit sur lui :
probe first luke
[self first-name last-name account solde]
Dans l'exemple ci-dessus, la liste renvoyée contient le mot self
référence à l'objet lui-même.
Vous pouvez exclure self de la liste en utilisant next :
probe next first luke
[first-name last-name account solde]
A présent, vous pouvez écrire une fonction qui va sonder le contenu
d'un objet :
probe-object: func [object][
foreach word next first object [
print rejoin [word ":" tab get in object word]
]
]
probe-object fred
first-name: Luke
last-name: Lakeswimmer
account: 89431
solde: $1204.52
En sondant les objets de cette façon, attention à éviter les boucles infinies !
Par exemple, si vous essayer de connaître certains objets qui contiennent des références à eux-mêmes,
votre code peut conduire à une boucle infinie.
C'est d'ailleurs la raison pour laquelle vous ne pouvez sonder l'objet system directement.
L'objet system contient en effet beaucoup de références à lui-même.
Updated 21-Jan-2005 - Copyright REBOL Technologies - Formatted with MakeDoc2 - Translation by Philippe Le Goff
|