EPOOL - Environnement de Programmation Orientée Objet en Lingo par Irv Kalb
Nous avons vu à quoi ressemble un objet, comment en créer un depuis un script parent, et comment les données et le code du script interagissent. Nous avons une base pour le fonctionnement d'un objet - mais pourquoi est-il si intéressant ? La réponse se trouve dans un concept fondamental de POO appelé encapsulation. L'encapsulation représente le fait qu'un objet est constitué de données (les propriétés) et de code (les méthodes) ensemble. Les données et le code qui modifie ces données se trouve réunis en un seul script. Ce concept se révèle très important et très puissant. C'est cela qui va vous permettre d'écrire du code très modulaire et réutilisable.
Vous pouvez imaginer un objet comme un récipient. C'est un contenant pour les données et le code en simultané. Mais de plus le récipient "possède" les propriétés en son sein. Souvenez-vous que la portée d'une propriété est limitée à l'acteur script dans lequel elle est définie. Ainsi seules les méthodes de l'objet lui-même peuvent accéder aux propriétés de cet objet (c'est à dire les lire ou les modifier). Si vous écrivez deux scripts parent A et B, seules les méthodes de A peuvent accéder aux propriétés définies dans le script A, et les méthodes de B aux propriétés de B. Si votre projet contient du code ailleurs, comme dans des gestionnaires dans un ou plusieurs scripts d'animation, ces gestionnaires ne peuvent accéder ni aux propriétés de A ni à celles de B. Ceci simplifie le débuggage. Si vous vous apercevez qu'une propriété d'un objet n'a pas une valeur attendue à cause d'une erreur de programmation, alors vous en déduisez que cette erreur doit se situer dans les méthodes du script où est définie cette propriété.
Comparons ça à des variables globales et des scripts d'animation. Les variables globales et les scripts qui peuvent les modifier n'ont aucune notion d'encapsulation. Par définition, une variable globale est accessible de n'importe quel gestionnaire dans le programme. Dans la pratique, déclarer et manipuler un grand nombre de variables globales peut vous amener à ce que j'appelle une "soupe globale". C'est à dire un nombre important de variables globales noyées dans un grand bol de code. Le code écrit pour manipuler des variables globales de cette manière est souvent appelé "code spaghetti" - un méli-mélo de gestionnaires emmêlés et très difficiles à détacher les uns des autres. Les programmes écrits de cette manière ont tendance à "grandir" sans être vraiment modélisés. Il se révèle souvent difficile de relire ce genre de programme parce qu'il n'y a aucune centralisation. Les gestionnaires peuvent modifier les données de n'importe où et quand ils veulent. Ce genre de programme produit souvent des erreurs parce que les programmeurs ne saisissent plus toutes les subtilités dans les interactions entre code et données puisque ces interactions sont éclatées dans tout le programme.
Avec l'accumulation de code et de variables globales que vous devez gérer lorsque votre programme progresse, vous réaliserez peut-être que vous auriez besoin d'un moyen d'approche qui vous permettrait d'organiser ou de grouper le code et les variables. Pour trouver une analogie, il vous suffit de regarder votre bureau. Imaginez que toutes les feuilles de papier soient placées sur votre bureau. Vous auriez un accès direct à tout, mais vous vous apercevriez qu'il est difficile de se rappeler alors où se trouve tel document et à quoi sert tel autre. Votre première réaction sera certainement de regrouper les feuilles parlant des même sujets et des les classer dans des dossiers. La situation dans l'ordinateur est exactement la même. Personne ne garde tout ses fichiers sur son bureau. Nous créons des dossiers et y déplaçons les fichiers pour classer le tout. Et par extension nous créons des dossiers dans des dossiers, etc.
En utilisant une approche similaire en essayant d'organiser votre code et vos variables, vous remarquerez que certaines portions de code et certaines données sont intimement liées. Un certain nombre de gestionnaires vont manipuler une ou plusieurs variables (eg, un groupe de gestionnaires qui effectuent des ajouts, des suppressions ou des recherches dans des listes). Vous pouvez aussi trouvez deux variables ou plus qui semblent logiquement liées (par exemple une liste et un index qui garde une trace de l'enregistrement "courant"). La première étape pour penser d'un manière POO est de discerner ces regroupements. C'est la base de départ pour modéliser vos programmes à partir d'objets.
Supposons par exemple que vous devez écrire un programme qui répond aux critères suivants. L'utilisateur doit pouvoir se déplacer entre différents "endroits" (identifiés comme des marqueurs de votre animation Director), et le programme doit être capable de restituer le cheminement de l'utilisateur. C'est-à-dire qu'il doit conserver un historique des endroits où est allé l'utilisateur si bien que celui-ci peut cliquer sur un bouton "retour" semblable à ce que vous pouvez trouver dans un navigateur internet. Une deuxième contrainte est de mémoriser ce qu'a vu l'utilisateur, si bien que si l'utilisateur quitte le programme et le redémarre, le programme puisse restaurer l'information de ce qui a déjà été vu. Ceci est habituellement fait en sauvegardant un fichier d'état et en le relisant au démarrage du programme.
Pour implémenter cette première contrainte vous avez besoin de données (une liste) qui a un rôle de pile des endroits récemment visités. Le code vous permet d'ajouter (empiler, push en anglais) un nouvel endroit dans l'historique des endroits visités, et de retirer (sortir de la pile, pop en anglais) le dernier endroit visité. Vous pouvez regrouper ce code et les données correspondantes dans un script parent qui définit l'objet de navigation, comme ci-dessous.
-- script Navigation
property plHistorique -- "Pile"
des endroits visités
on new me
plHistorique = []
return me
end
-- "Empile" un nouvel endroit sur la pile
on mNouvelEndroit me, nomDuNouvelEndroit
add(plHistorique, nomDuNouvelEndroit)
end
-- "Depile" le dernier endroit visité, et s'y rend
on mRetour me
nEndroits = count(plHistorique)
if nEndroits = 0
then
alert("Nulle part où retourner")
return
end if
endroitPrecedent = plHistorique[nPlaces]
deleteAt(plHistorique, nEndroits)
go endroitPrecedent
end
Vous avez aussi besoin de données pour mémoriser
les endroits déjà visités: une liste des noms des endroits
où l'utilisateur s'est rendu. Ensuite nous avons besoin de code pour
lire et écrire les informations dans un fichier. Ce code et ces données
peuvent être logiquement regroupés dans un deuxième script
parent d'objet de traçage (tracking).
-- Tracking script
property plEndroits -- propriété
: liste des endroits déjà visités
property pNomFichierTracking --
le nom du fichier utilisé pour le tracking
on new me
pNomFichierTracking = "tracking"
-- peut aussi être passé en variable
endroits = getPref(pNomFichierTracking)
-- Si le fichier n'est pas trouvé, alors on initialise une
liste vide
if voidp(endroits) then
plEndroits = []
else
plEndroits = value(endroits)
end if
return me
end
-- Si l'endroit n'est pas dans la liste on l'ajoute
on mVisite me, cetEndroit
if getOne(plEndroits,
cetEndroit) = 0 then
add(plEndroits, cetEndroit)
end if
end
-- Sauvegarde l'état courant de la visite
on mSauve me
setPref(pNomFichierTracking, string(plEndroits))
end
Ces deux objets, l'objet de navigation et l'objet de tracking, n'ont pas de relation l'un avec l'autre. Chaque script parent est responsable de son propre petit monde. Tout le code qui gère la navigation est regroupé dans un seul script parent, et tout le code qui gère le tracking est encapsulé dans un second script parent. Vous pouvez vous représenter chacun de ces deux objets comme des petits programmes qui agissent indépendamment en réponse à des messages reçus. En codant ces objets de cette manière, ils deviennent très versatiles, c'est à dire facilement réutilisables dans d'autres programmes. Encapsuler comme ceci les éléments aide aussi beaucoup lors du débuggage. Si une erreur se produit dans la navigation, nous savons que le problème doit provenir de l'objet Navigation ou des endroits qui font appel à des actions de cet objet. De même si une erreur se produit dans le tracking, alors le coupable doit être dans l'objet tracking ou dans les appels qui lui sont faits.
En tant que programmeur qui écrit du code orienté objet, vous vivez dans deux mondes; "l'intérieur" de l'objet et "l'extérieur" de l'objet. Ces monde sont bien distincts et se révèlent très différents, chacun ayant sa propre vision de ce qui peut ou ne peut pas être fait. Pour bien comprendre la POO, vous devez vous habituer à vivre et travailler dans ces deux mondes et vous devez devenir capables de changer de point de vue instantanément.
En tant que modéliseur et développeur d'un objet, vous travailler "à l'intérieur" de l'objet. Votre rôle est de définir certaines propriétés et leurs méthodes pour répondre à un certain nombre de critères. Dans notre premier exemple, notre objet répondait à un critère de gestion de compte en banque. En tant que développeur vous décidez des propriétés nécessaires pour représenter les données et conserver l'état de l'objet. Vous développez aussi les algorithmes pour manipuler les données et transcrivez ces algorithmes dans les méthodes de l'objet. En tant que développeur vous écrivez le code pour décrire comment l'objet fait ce qu'il fait. Les méthodes de l'objet qui sont accessibles et susceptibles d'être appelées de l'extérieur de l'objet sont regroupées sous le terme d'application program interface (plus souvent appelée API, et parfois simplement l'interface) de l'objet.
Cependant hors de l'objet vous êtes un utilisateur (ou un client) de l'objet. Vous ne pouvez communiquer avec l'objet seulement que par les méthodes définies dans l'API de l'objet. Comme je l'ai déjà dit, quand vous êtes hors de l'objet, vous "n'avez pas le droit" de regarder les détails de l'implémentation. Vous devez ignorer comment l'objet effectue ses opérations, et vous reposer uniquement sur l'API que l'objet vous propose. Vous devez partir du principe que l'objet est autonome et indépendant.
Si vous avez déjà utilisés des XTRAs de Director, alors vous avez déjà connu ce fonctionnement. Tous les Xtras ont une API qui est l'ensemble des routines susceptibles d'être appelées depuis du code Lingo. Par exemple nous pouvons regarder l'Xtra standard FileIO. Les principales fonctions de cet Xtra permettent aux programmeurs de lire et d'écrire des fichiers sur le disque dur de l'utilisateur. Depuis la fenêtre de message de Director, nous pouvons tester :
-- Bienvenue dans Director --
put interface(xtra "FileIO")
-- "xtra fileio -- CH 18apr97
new object me -- create a new child instance
-- FILEIO --
fileName object me -- return fileName string of the open file
status object me -- return the error code of the last method called
error object me, int error -- return the error string of the error
setFilterMask object me, string mask -- set the filter mask for dialogs
openFile object me, string fileName, int mode -- opens named file. valid modes:
0=r/w 1=r 2=w
closeFile object me -- close the file
displayOpen object me -- displays an open dialog and returns the selected fileName
to lingo
displaySave object me, string title, string defaultFileName -- displays save
dialog and returns selected fileName to lingo
etc.
Voici l'API de l'Xtra FileIO. C'est l'ensemble des routines disponibles à l'utilisateur de l'Xtra. Nous sommes "à l'extérieur" de l'Xtra, et nous ne nous intéressons pas à la façon dont ces fonctions sont implémentées dans l'Xtra. Nous ne nous préoccupons pas des variables et des algorithmes qu'il utilise pour implémenter les appels à ses méthodes. En fait il existe des versions Mac et Windows de FileIO qui contiennent du code sensiblement différents puisqu'ils communiquent avec des systèmes d'exploitation bien distincts. Cependant, puisque nous disposons d'une API définie, nous pouvons ne pas nous soucier de ces problèmes. Nous faisons confiance à FileIO pour qu'il exécute les bonnes actions lorsque nous faisons appel à ses méthodes. Voici un exemple de comment utiliser l'Xtra FileIO pour lire un texte depuis un fichier. Ceci est directement tiré de la Technote Macromdéia #3192 :
Le gestionnaire suivant lit un fichier texte et place ce texte dans un acteur champ nommé monChamp. La méthode displayOpen permet à l'utilisateur de séléctionner un fichier via une boîte de dialogue standard Macintosh ou Windows.
on
litDepuisFichier
if objectP(monFichier) then set monFichier = 0 -- Libère l'instance si elle existe déjà
set monFichier = new(xtra "fileio") -- Crée une nouvelle
instance de FileIO
if the machinetype = 256 then
setfiltermask (monFichier, "Tous
les fichiers,*.*,Fichiers texte,*.txt") -- définit le masque de fichier (Win)
else
setfiltermask (monFichier, "TEXT")
-- définit le masque de fichier (Mac)
end if
set nomFichier = displayOpen(monFichier) --
affiche la boîte de dialogue "Ouvrir"
if not voidP(nomFichier) and not (nomFichier = EMPTY) then
openFile(monFichier, nomFichier, 1) -- ouvre le fichier séléctionné par l'utilisateur
if status(monFichier) = 0
then
set leContenu = readFile(monFichier) --
Place le contenu du fichier dans une variable Lingo
put leContenu
into field "myField" -- affiche le texte dans un
champ
else
alert error(monFichier,status(monFichier)) --
affiche un message d'erreur
end if
end if
closeFile(monFichier) -- Feme le fichier
set monFichier = 0
-- Libère l'instance
end
Remarquez que la façon d'utiliser un Xtra est exactement la même que pour appeler les méthodes d'un objet. Vous créez une instance de l'Xtra à partir de sa méthode "new", vous utilisez les méthodes de l'Xtra FileIO (ici setfiltermask, displayOpen, openFile, status, readFile, et closeFile), et vous libérez l'instance lorsque vous avez fini votre traitement.
Voici une analogie simple à partir du monde réel. Imaginez votre télévision et sa télécommande. La télévision peut-être vue comme un objet et la télécommande est à l'extérieur de l'objet télévision, vous donnant me moyen d'agir sur l'objet télévision. Quand vous appuyez sur la télécommande pour passer à la chaîne suivante, vous voyez que la télévision change effectivement de chaîne. Mais regardons cette action au ralenti. La télécommande envoie juste un message à la télévision lui disant d'aller à la chaîne suivante. La télécommande ne connaît pas la chaîne actuelle - elle n'a pas envie de la savoir et n'en a pas besoin. La télécommande ne sait pas non plus comment la télévision change de chaîne, elle se contente de lui demander d'aller à la chaîne suivante. La télévision conserve en interne une trace de la chaîne courante et une liste des chaîne disponibles. Lorsque la télévision reçoit une demande pour aller à la chaîne suivante, elle va à la chaîne suivante qu'elle connaît à partir de ces données. On retrouve la même idée pour régler le volume. Lorsque vous montez le son sur la télécommande, celle-ci envoie un message à la télévision lui demandant de monter le niveau. La télévision sait quel est le niveau courant et fait ce dont elle a besoin pour le monter. Le bouton 'mute' est simplement un bouton poussoir (deux états, on ou off). Lorsque vous pressez le bouton mute de la télécommande, elle envoie un message à la télévision lui demandant de basculer l'état de sa variable mute. La télévision sait si mute est actuellement on ou off, et ainsi quand il reçoit le message il peut basculer cet état.
En fait le concept d'envoi de message est si fondamental que le terme "envoyer un message" est synonyme de "appeler une méthode de l'objet". En POO vous rencontrerez aussi "demander à l'objet de faire". Ces trois terminologies sont totalement interchangeables.
Traditionnellement la vue extérieure d'un objet est une boîte noire. Un objet est vu comme "noir" puisque vous n'avez pas le droit de voir ce qu'il y a à l'intérieur. Le fonctionnement interne de la boîte vous est caché. Vous faites appel aux méthodes de l'objet, et l'objet fait ce qu'il doit pour répondre à ces messages. Nous arrivons à un point intéressant et important. Si vous imaginez l'objet comme une boîte noire qui se contente de répondre à certaines méthodes, alors vous pouvez facilement remplacer la boîte noire par une autre boîte noire qui répond aux même messages.
Voici un autre gros intérêt de la POO. Tant que vous conservez l'API d'un objet, son code interne peut changer à volonté sans que vous en ressentiez le moindre effet dans le reste du programme. Cette propriété vous permet à vous - programmeur - de délimiter des sections de projet et de travailler dessus indépendamment du reste du projet. Cela vous donne un moyen de compartimenter de grands morceaux de logiciel et des morceaux plus petits. Chaque morceau est responsable d'une part précise du programme complet. Cette modularité permet à une équipe de travailler indépendamment sur différentes pièces d'un même projet. La clé est dans l'entente est la constance sur la définition de l'API.
Bien entendu si un élément de l'API d'un objet est modifié, alors tous les utilisateurs de l'objet doivent s'assurer que tous les appels qu'ils font à cet élément ont été rectifiés. Si vous ajouter un paramètre à une méthode, changez l'ordre des paramètres, etc, alors tous les utilisateurs de votre objet doivent faire des modifications pour rester synchrones avec votre version.
L'idée clé à retenir est que tant que l'API d'un objet n'est pas modifiée, vous êtes libre de changer l'implémentation à l'intérieur de cet objet sans que la différence change quelque chose dans le programme, à l'extérieur de l'objet. A l'intérieur de l'objet vous pouvez changer le nom des propriétés, changer les algorithmes pour améliorer les performances, changer le type de propriétés pour conserver des traces des données de l'objet - tout cela sans vous inquiéter d'une incidence parasite de vos modifications quelque part ailleurs dans le programme.
METHODES D'ACCES
Parfois lorsque vous êtes à l'extérieur d'un objet, vous avez besoin de connaître la valeur d'une de ses propriétés. Pour revenir à notre exemple de compte en banque il est évident que de l'extérieur de 'objet CompteEnbanque nous voulons connaître le solde du compte. Dans le code de l'objet CompteEnBanque il y a une propriété pSolde qui contient le solde courant. Dans un cas comme celui-là, vous pouvez penser que vous pouvez utiliser un "accès direct" à la propriété depuis l'extérieur de l'objet. En fait Lingo vous permet de récupérer la valeur des propriétés d'un objet directement. L'exemple qui suit est légal et fonctionne parfaitement:
x = the pSole of goCompteEnBanque
ou en utilisant la syntaxe pointée :
x = goCompteEnBanque.pSolde
De ma voix la plus forte je vous encourage à ne pas faire un tel accès direct et je vais bientôt expliquer pourquoi. En codant proprement, cela devrait être interdit, déclaré hors la loi et même inimaginable. Cette syntaxe n'est appropriée que dans un seul cas : le débuggage. Si vous débuggez un objet, il est correct de connaître la valeur d'une propriété en tapant :
put goCompteEnBanque.pSolde
Alors quel est le moyen correct d'accéder à la valeur d'une propriété ? Si le code à l'extérieur d'un objet doit accéder à une de ses propriété, alors l'objet devra contenir une ou plusieurs méthodes spécialement écrites pour cette propriété, appelées méthodes d'accès. Ces méthodes sont parfois appelées "getters" et "setters" puisque c'est ce qu'elles font. Une méthode "getter" se contente de renvoyer (get) la valeur de la propriété, alors qu'une méthode "setter" définit la valeur d'une propriété. En général les méthodes "getter" et "setter" pour une propriété d'un objet ressemblent à ceci (ici on manipule une propriété nommée "pUnePropriete"):
on
mGetUnePropriete me
return pUnePropriete
end
on mSetUnePropriete me, nouvelleValeur
pUnePropriete = nouvelleValeur
end
une méthode d'accès est la passerelle que l'objet fournit
pour permettre à du code extérieur de manipuler un de ses propriétés.
Par exemple dans notre exemple d'objet compte en banque, une méthode
d'accès simple aurait pu être écrite comme ceci :
on
mSetSolde me, nouveauSolde
pSolde = nouveauSolde
end
on mGetSolde
me
return pSolde
end
Maintenant que la méthode d'accès est écrite, quand vous avez besoin de connaître le solde courant, vous écrivez :
leSolde = goCompteEnBanque.mGetSolde()
Réciproquement, si vous voulez définir un nouveau solde vous faites :
goCompteEnBanque.mNouveauSolde(unNouveauSolde)
Cette couche de code supplémentaire peut sembler triviale ou même inutilement lourde. Cependant c'est ce qui garantit le concept clé d'encapsulation en assurant que les objets modifient eux-mêmes leurs données. L'utilisation des méthodes d'accès sépare l'implémentation de la représentation interne. Il y a deux raisons pour lesquelles écrire et utiliser des méthodes d'accès est vital.
Retournons à notre objet compte en banque pour deux exemples. D'abord imaginons que nous avons une grande quantité de code manipulant un objet CompteEnBanque. Continuons en supposant que nous avons écrit plusieurs lignes telles que :
leSolde = goCompteEnBanque.pSolde
Alors, parce que vous trouvez ça plus parlant, vous décidez de changer le nom de la propriété "pSolde" en "pMontant". Alors vous ouvrez le script parent CompteEnBanque, et changer toutes les occurences de pSolde pour les remplacer par pMontant. Mais vous avez alors à chercher également à travers tout votre code pour trouver tous les endroits où vous avez écrit des lignes comme celles ci-dessus pour les remplacer par une ligne du style :
leSolde = goCompteEnBanque.pMontant
Et comme vous utilisez peut-être aussi des propriétés pSolde dans d'autres objets, vous devrez vérifier chaque ligne qui contient pSolde et décider ligne par ligne si vous devez ou non le remplacer par pMontant. Ceci peut vous mener à des erreurs imprévues et difficile à repérer. Maintenant considérons ce qui se serait passé si vous aviez utilisé des méthodes d'accès telles que mGetSolde. Dans ce cas tout ce que vous aurez eu à faire était le changement dans le script parent CompteEnBanque. La méthode d'accès :
on
mGetSolde me
return pSolde
end
devient :
on mGetSolde
me
return pMontant
end
Et vous auriez terminé. L'utilisation de méthodes d'accès vous permet de modifier l'objet sans en affecter quoi que ce soit à l'extérieur.
Ensuite il y a une raison encore plus importante d'utiliser des méthodes d'accès. Imaginez que dans notre compte en banque nous gardions une valeur du taux d'intérêt et effectuons des calculs basés sur ce taux:
property pTauxDInterets
on new me, soldeInitial, motDePasse,
tauxDInteret ...
pTauxDInterets = interestRate
-- etc.
De plus il y a de nombreux endroits d'où vous voulez connaître le taux d'intérêt de l'objet, et vous avez écrit :
x = goCompteEnBanque.pTauxDInteret
Maintenant avec l'évolution du programme, les critères changent. A la place d'un taux constant, le taux d'intérêt évolue en fonction du solde courant. Si vous avez moins de $1000 sur votre compte, le taux d'intérêts est de 3%. Si votre solde est entre $1000 et $5000 le taux d'intérêt et de 4%. Si votre solde est de plus de 5000%, votre taux d'intérêt est de 5%. Si vous aviez utilisé un accès direct comme ci-dessus, vous devriez changer toutes ces lignes pour implémenter une formule. A la place, si vous aviez utilisé une méthode d'accès, vous auriez simplement changé l'objet :
on
mGetTauxDInteret me
if pSolde < 1000
then
tauxDInteret = 3
else if pSolde
< 5000 then
tauxDInteret = 4
else
tauxDInteret = 5
end if
return tauxDInteret
end
Code utilitaire
Ce concept de regrouper les propriétés et les méthodes est si fondamental qu'il est insensé d'exécuter une méthode d'un objet sans ses propriétés. En fait si une méthode d'un objet ne se réfère à aucune des propriétés de l'objet, alors cette méthode ne devrait probablement pas être dans un objet. Le code de ce genre est appelé code utilitaire et se situe dans un autre acteur. Par exemple supposons que nous voulons forcer une chaîne à être en majuscules ou en minuscules. Voici du code qui implémente ces fonctions (alphabet américain : les accents, cédilles et autres signes diacritiques ne sont pas pris en compte):
on
ConvertiEnMinuscule charIn
set valOfCharIn = charToNum(charIn)
if valOfCharIn >= 65 then
-- 65 est "A"
if valOfCharIn <= 90
then -- 90 est "Z"
set valLower = valOfCharIn + 32 -- 32 est la différence entre
les majuscules et les minuscules
set charOut =
numToChar(valLower)
return charOut
end if
end if
-- Passé à travers, donc le caractère n'est
pas en majuscule
return charIn
end
on ConvertiEnMajuscule
charIn
set valOfCharIn = charToNum(charIn)
if valOfCharIn >= 97 then
-- 65 est "a"
if valOfCharIn <= 122
then -- 122 est "z"
set valUpper = valOfCharIn - 32 -- 32 est la différence entre
les majuscules et les minuscules
set charOut =
numToChar(valUpper)
return charOut
end if
end if
-- Passé à travers, donc le caractère n'est
pas en minuscule
return charIn
end
on ConvertiChaineEnMinuscule chaineIn
set nChars = length(chaineIn)
set chaineOut = ""
repeat with
charIndex = 1 to nChars
set thisChar = char
charIndex of chaineIn
set chaineOut
= chaineOut & ConvertCharToLowerCase(thisChar)
end repeat
return chaineOut
end
on ConvertiChaineEnMajuscule
chaineIn
set nChars = length(chaineIn)
set chaineOut = ""
repeat with
charIndex = 1 to nChars
set thisChar = char
charIndex of chaineIn
set chaineOut
= chaineOut & ConvertCharToUpperCase(thisChar)
end repeat
return chaineOut
end
Cette fonctionnalité est universelle - elle ne doit pas être restreinte à fonctionner à l'intérieur d'un seul objet en particulier. Les gestionnaires ConvertiChaineEnMinuscule et ConvertiChaineEnMajuscule acceptent une chaîne en entrée et retournent une chaîne. Voici un exemple parfait de techniques de "code structuré". Le gestionnaire n'agit que sur les données qui lui sont passées en argument - il n'y a aucune référence à des données extérieures. Ces gestionnaires peuvent être appelés par n'importe quelle méthode de n'importe quel objet ou par d'autres scripts d'animation. Par conséquent je créerai un acteur script d'animation appelé "utilitaires" et y placerai ce code.