=encoding iso-8859-1 =head1 NOM perlobj - Objets de Perl =head1 DESCRIPTION Tout d'abord, vous devez comprendre ce que sont les références en Perl. Voir L pour cela. Ensuite, si vous trouvez encore le travail qui suit sur les références trop compliqué, un tutoriel sur la programmation orientée objet en Perl se trouve dans L et L. Si vous êtes toujours avec nous, voici trois définitions très simples que vous devriez trouver rassurantes. =over 4 =item 1. Un objet est simplement une référence qui sait à quelle classe elle appartient. =item 2. Une classe est simplement un paquetage qui fournit des méthodes pour manipuler les références d'objet. =item 3. Une méthode est simplement un sous-programme qui attend une référence d'objet (ou un nom de paquetage, pour les méthodes de classe) comme premier argument. =back Nous allons maintenant couvrir ces points plus en détails. =head2 Un Objet est Simplement une Référence Contrairement, disons, à C++, Perl ne fournit aucune syntaxe particulière pour les constructeurs. Un constructeur est juste un sous-programme qui retourne une référence à quelque chose qui a été "consacré" pour devenir une classe, généralement la classe dans laquelle le sous-programme est défini. Voici un constructeur typique : package Critter; sub new { bless {} } Ce mot-clé C n'a rien de spécial. Vous auriez aussi bien pu écrire un constructeur de cette façon : package Critter; sub spawn { bless {} } Ceci pourrait même être préférable, car les programmeurs C++ n'auront pas tendance à penser que C fonctionne en Perl de la même manière qu'en C++. Car ce n'est pas le cas. Nous vous recommandons de nommer vos constructeurs de façon qu'ils aient un sens en fonction du contexte du problème que vous résolvez. Par exemple, les constructeurs dans l'extension Tk de Perl portent les noms des widgets qu'ils créent. Une différence entre les constructeurs de Perl et de C++ est qu'en Perl, ils doivent allouer leur propre mémoire (L'autre différence est qu'ils n'appellent pas automatiquement les constructeurs de classe de base surchargés). Le C<{}> alloue un hachage anonyme ne contenant aucune paire clé/valeur, et le retourne. Le bless() prend cette référence et dit à l'objet qu'il référence qu'il est désormais un Critter, et retourne la référence. C'est pour que cela soit plus pratique, car l'objet référencé sait lui-même qu'il a été consacré, et sa référence aurait pu être retournée directement, comme ceci : sub new { my $self = {}; bless $self; return $self; } Vous voyez souvent de telles choses dans des constructeurs plus compliqués qui veulent utiliser des méthodes de la classe pour sa construction : sub new { my $self = {}; bless $self; $self->initialize(); return $self; } si vous vous souciez de l'héritage (et vous devriez ; voir L), alors vous préférerez utiliser la forme à deux arguments de bless pour que vos constructeurs puissent être hérités : sub new { my $class = shift; my $self = {}; bless $self, $class; $self->initialize(); return $self; } Ou si vous vous attendez à ce que les gens appellent non pas seulement C<< CLASS->new() >>, mais aussi C<<$obj->new() >>, alors utilisez quelque chose comme ceci. La méthode initialize() sera celle de la classe dans laquelle nous consacrons l'objet : sub new { my $this = shift; my $class = ref($this) || $this; my $self = {}; bless $self, $class; $self->initialize(); return $self; } À l'intérieur du paquetage de la classe, les méthodes gèreront habituellement la référence comme une référence ordinaire. À l'extérieur du paquetage, la référence est généralement traitée comme une valeur opaque à laquelle on ne peut accéder qu'à travers les méthodes de la classe. Bien qu'un constructeur puisse en théorie re-consacrer un objet référencé appartenant couramment à une autre classe, ceci va presque certainement vous causer des problèmes. La nouvelle classe est responsable de tout le nettoyage qui viendra plus tard. La précédente consécration est oubliée, puisqu'un objet ne peut appartenir qu'à une seule classe à la fois (même si bien sûr il est libre d'hériter de méthodes en provenance de nombreuses classes). Si toutefois vous vous retrouvez dans l'obligation de le faire, la classe parent a probablement un mauvais comportement. Une clarification : les objets de Perl sont consacrés. Les références ne le sont pas. Les objets savent à quel paquetage ils appartiennent. Pas les références. La fonction bless() utilise la référence pour trouver l'objet. Considérez l'exemple suivant : $a = {}; $b = $a; bless $a, BLAH; print "\$b is a ", ref($b), "\n"; Ceci reporte $b comme étant un BLAH, il est donc évident que bless() a agi sur l'objet et pas sur la référence. =head2 Une Classe est Simplement un Paquetage Contrairement à, disons, C++, Perl ne fournit aucune syntaxe spéciale pour les définitions de classes. Vous utilisez un paquetage en tant que classe en mettant des définitions de méthodes dans la classe. Il existe un tableau spécial appelé @ISA à l'intérieur de chaque paquetage, qui dit où trouver une méthode si on ne la trouve pas dans le paquetage courant. C'est la façon qu'a Perl d'implémenter l'héritage. Chaque élément du tableau @ISA est juste le nom d'un autre paquetage qui s'avère être un paquetage de classe. Les classes sont fouillées (en profondeur tout d'abord) à la recherche des méthodes manquantes dans l'ordre où elles apparaissent dans @ISA. Les classes accessibles à travers @ISA sont les classes de base de la classe courante. Toutes les classes héritent implicitement de la classe C en tant que dernière classe de base. Plusieurs méthodes couramment utilisées sont automatiquement fournies par la classe UNIVERSAL ; voir L<"Méthodes UNIVERSAL par défaut"> pour plus de détails. Si une méthode manquante est trouvée dans une classe de base, elle est mise en cache dans la classe courante pour plus d'efficacité. Modifier @ISA ou définir de nouveaux sous-programmes invalide le cache et force Perl à recommencer la recherche. Si ni la classe courante, ni ses classes de base nommées, ni la classe UNIVERSAL ne contiennent la méthode requise, ces trois endroits sont fouillés de nouveau, cette fois à la recherche d'une méthode appelée AUTOLOAD(). Si une AUTOLOAD est trouvée, cette méthode est appelée au nom de la méthode manquante, remplaçant le nom complet de la méthode qui devait être appelée par le paquetage global $AUTOLOAD. Si rien de tout cela ne marche, Perl abandonne finalement et se plaint. Si vous voulez stopper l'héritage par AUTOLOAD à votre niveau, il vous suffit de dire : sub AUTOLOAD; et l'appel mourra via die en utilisant le nom de la méthode appelée. Les classes de Perl ne font que de l'héritage de méthodes. L'héritage de données est laissé à la charge de la classe elle-même. Ce n'est pas, et de loin, un problème en Perl, car la plupart des classes modèlent les attributs de leurs objets en utilisant un hachage anonyme, qui sert comme un petit espace de noms propre qui sera ciselé par les diverses classes qui pourront vouloir faire quelque chose de l'objet. Le seul problème avec ceci est que vous ne pouvez pas être certain que vous n'utilisez pas un morceau du hachage qui ne soit pas déjà utilisé. Une façon raisonnable de le contourner est d'ajouter votre espace de noms au hachage avec le nom du paquetage. sub bump { my $self = shift; $self->{ __PACKAGE__ . ".count"}++; } =head2 Une Méthode est Simplement un Sous-programme Contrairement, disons, à C++, Perl ne fournit aucune syntaxe spéciale pour la définition des méthodes (Il fournit toutefois un peu de syntaxe pour l'invocation des méthodes. Vous en saurez plus à ce sujet plus tard). Une méthode s'attend à ce que son premier argument soit l'objet (référence) ou le paquetage (chaîne) pour lequel elle est invoquée. Il existe deux façons d'appeler les méthodes, que nous appellerons des méthodes de classe et des méthodes d'instance. Une méthode de classe attend un nom de classe comme premier argument. Elle fournit une fonctionnalité à la classe toute entière, et pas à un objet en particulier appartenant à cette classe. Les constructeurs sont souvent des méthodes de classe, mais voyez L et L pour des alternatives. De nombreuses méthodes de classe ignorent tout simplement leur premier argument, car elles savent déjà dans quel paquetage elles sont, et se moquent du paquetage via lequel elles ont été invoquées (ce ne sont pas nécessairement les mêmes, car les méthodes de classe suivent l'arbre d'héritage tout comme les méthodes d'instanciation ordinaires). Un autre usage typique des méthodes de classe est la recherche d'un objet par son nom : sub find { my ($class, $name) = @_; $objtable{$name}; } Une méthode d'instanciation attend une référence à un objet comme premier argument. Typiquemenet, elle change le premier argument en variable "self" ou "this", puis l'utilise comme une référence ordinaire. sub display { my $self = shift; my @keys = @_ ? @_ : sort keys %$self; foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } } =head2 Invocation de Méthode Il existe deux façons d'invoquer une méthode, une qui vous est déjà familière, et l'autre qui va vous paraître familière. Perl 4 avait déjà une syntaxe d'"objet indirect" que vous utilisez quand vous dites print STDERR "help!!!\n"; Cette même syntaxe peut être utilisée pour appeler des méthodes de classe ou d'instanciation. Nous utiliserons les deux méthodes définies ci-dessus, la méthode de classe pour rechercher une référence d'objet et la méthode d'instanciation pour afficher ses attributs. $fred = find Critter "Fred"; display $fred 'Height', 'Weight'; Elles pourraient être combinées pour former une seule instruction en utilisant un BLOC à l'emplacement de l'objet indirect : display {find Critter "Fred"} 'Height', 'Weight'; Pour les fans de C++, il existe aussi une syntaxe utilisant la notation -> qui fait exactement la même chose. Les parenthèses sont requises s'il y a un argument. $fred = Critter->find("Fred"); $fred->display('Height', 'Weight'); ou en une seule instruction, Critter->find("Fred")->display('Height', 'Weight'); Parfois la première syntaxe est plus lisible, dans d'autres cas, c'est la deuxième qui l'est. La syntaxe d'objet indirect fait moins fouillis, mais elle a la même ambiguïté que les opérateurs de listes ordinaires. Les appels de méthodes d'objet indirect sont habituellement analysés en utilisant la même règle que les opérateurs de liste : "si cela a l'air d'une fonction, c'est une fonction" (On présume pour le moment que vous pensez que deux mots qui se suivent ont l'air d'un nom de fonction. Les programmeurs C++ semblent le penser avec une certaine régularité, en particulier lorsque le premier mot est "new"). Ainsi, les parenthèses de new Critter ('Barney', 1.5, 70) sont présumées entourer TOUS les arguments de l'appel de méthode, sans tenir compte de ceux qui les suivent. Dire new Critter ('Bam' x 2), 1.4, 45 serait équivalent à Critter->new('Bam' x 2), 1.4, 45 qui n'est probablement pas ce que vous désirez. Toutefois, de façon troublante, cette règle ne s'applique que lorsque l'objet indirect est un 'bareword' servant de nom de paquetage, et pas lorsque c'est un scalaire, un BLOC, ou un nom de paquetage qualifié par C. Dans ces cas, les arguments sont analysés de la même façon qu'un opérateur de liste d'objet indirect comme print, donc new Critter:: ('Bam' x 2), 1.4, 45 est la même chose que Critter::->new(('Bam' x 2), 1.4, 45) Pour plus de raisons expliquant pourquoi la syntaxe d'objet indirect est ambigüe, voir l'L<"AVERTISSEMENT"> ci-dessous. Vous désirerez parfois spécifier quelle méthode de classe utiliser. Là, vous pouvez appeler votre méthode comme un sous-programme ordinaire, en vous assurant bien de passer le premier argument requis explicitement : $fred = MyCritter::find("Critter", "Fred"); MyCritter::display($fred, 'Height', 'Weight'); Contrairement aux appels de méthode, les appels de fonction ne prennent pas l'héritage en considération. Si vous voulez simplement indiquer à Perl qu'il devrait I par chercher une méthode dans un paquetage particulier, utilisez un appel de méthode ordinaire, mais qualifiez le nom de la méthode avec celui du paquetage comme ceci : $fred = Critter->MyCritter::find("Fred"); $fred->MyCritter::display('Height', 'Weight'); Si vous essayez de contrôler où la recherche de méthode doit commencer I que votre code s'exécute dans la classe elle-même, vous pouvez dans ce cas utiliser la pseudo classe SUPER, qui dit de commencer à chercher dans la liste @ISA de votre classe de base, sans que vous ayez à la nommer explicitement : $self->SUPER::display('Height', 'Weight'); Notez bien que la construction C Ia de sens Ià l'intérieur de la classe. Vous voudrez parfois appeler une méthode à un moment où vous ne pouvez pas encore connaître son nom. Vous pouvez alors utiliser la flèche, remplaçant le nom de la méthode par une simple variable scalaire le contenant ou par une référence à la fonction. $method = $fast ? "findfirst" : "findbest"; $fred->$method(@args); # appel par nom if ($coderef = $fred->can($parent . "::findbest")) { $self->$coderef(@args); # appel par coderef } =head2 AVERTISSEMENT Même si la syntaxe d'objet indirect peut être attirante pour les anglophones et les programmeurs C++, ne soyez pas séduit ! Elle souffre de deux problèmes graves. Le premier est qu'un objet indirect est limité à un nom, une variable scalaire ou un bloc, parce qu'il y aurait autrement trop de recherche préalable à faire, tout comme n'importe quel autre déréférencement postfixe dans le langage (les mêmes règles bizarres sont utilisées pour l'emplacement du handle de fichier dans les fonctions telles que C et C). Ceci peut mener à des problèmes de précédence horriblement troublants, comme dans ces deux lignes : move $obj->{FIELD}; # probablement faux ! move $ary[$i]; # probablement faux ! Elles s'analysent en vérité de façon surprenante : $obj->move->{FIELD}; # Well, lookee here $ary->move->[$i]; # Vous ne vous attendiez pas à celle-ci, hein ? Au lieu de ce à quoi vous auriez pu vous attendre : $obj->{FIELD}->move(); # You should be so lucky. $ary[$i]->move; # Ouais, effectivement. La partie gauche de ``-E'' n'est pas aussi limitée, parce que c'est un opérateur infixe et non postfixe. Comme si ce n'était pas suffisamment mauvais, réfléchissez à ceci : Perl doit deviner I si C et C sont des fonctions ou des méthodes. Habituellement Perl trouve la bonne solution, mais quand ce n'est pas le cas, vous obtenez un appel à une fonction compilé comme une méthode, ou vice versa. Ceci peut introduire des bugs subtils difficiles à démasquer. Par exemple, appeler une méthode C dans une notation indirecte - comme les programmeurs C++ ont tant coutume de le faire - peut être mal compilé et devenir un appel à un sous-programme s'il existe déjà une fonction C en vue. Vous vous retrouvez en train d'appeler le C du paquetage courant comme un sous-programme, au lieu de la méthode de classe désirée. Le compilateur essaye de tricher en se souvenant des Cs de bareword (? NDT), mais la peine encourue s'il se trompe ne vaut tout simplement pas les années de débogage qu'il vous faudra pour corriger des bugs aussi subtils. La notation de flèche infixe utilisant ``C<-E>'' ne souffre pas de ces ambiguïtés gênantes, nous vous conseillons donc de l'utiliser de façon exclusive. =head2 Méthodes UNIVERSAL par défaut Le paquetage C contient automatiquement les méthodes suivantes qui sont héritées par toutes les autres classes : =over 4 =item isa(CLASSE) C retourne I si son objet est consacré en une sous-classe de C C est aussi exportable et peut être appelée comme un sous-programme avec deux arguments. Ceci permet de vérifier vers quoi pointe une référence. Exemple use UNIVERSAL qw(isa); if(isa($ref, 'ARRAY')) { #... } =item can(METHODE) C vérifie si son objet possède une méthode appelée C, si c'est le cas, une référence à la routine est retournée, sinon c'est I qui est renvoyé. =item VERSION( [NEED] ) C retourne le numéro de version de la classe (du paquetage). Si l'argument NEED est fourni, elle vérifie que le numéro de version courant (tel que défini par la variable $VERSION dans le paquetage donné) n'est pas inférieur à NEED ; il mourra si ce n'est pas le cas. Cette méthode est normalement appelée en tant que méthode de classe. Elle est appelée automatiquement par la forme C de C. use A 1.2 qw(des routines importees); # implies: A->VERSION(1.2); =back B C utilise directement le code interne de Perl pour la recherche de méthode, et C utilise une méthode très similaire et une stratégie de cache. Ceci peut produire des effets étranges si le code de Perl change dynamiquement @ISA dans un des paquetages. Vous pouvez ajouter d'autres méthodes à la classe UNIVERSAL via du code Perl ou XS. Vous n'avez pas besoin de préciser C pour que ces méthodes soient disponibles dans votre programme. Ce n'est nécessaire que si vous désirez qu'C soit disponible comme un sous-programme normal dans le paquetage courant. =head2 Destructeurs Lorsque la dernière référence à un objet disparaît, l'objet est automatiquement détruit (Ce qui peut même se produire après que vous soyez sorti, si vous avez stocké des références dans des variables globales). Si vous voulez prendre le contrôle juste avant que l'objet ne soit libéré, vous pouvez définir une méthode DESTROY dans votre classe. Elle sera appelée automatiquement au moment approprié, et vous pourrez y réaliser tous les nettoyages supplémentaires dont vous avez besoin. Perl passe une référence à l'objet qui va être détruit comme premier (et unique) argument. Souvenez-vous que cette référence est une valeur en lecture seule, qui ne peut pas être modifiée en manipulant C<$_[0]> au sein du destructeur. L'objet en lui-même (i.e. le bidule vers lequel pointe la référence, appelé C<${$_[0]}>, C<@{$_[0]}>, C<%{$_[0]}> etc.) n'est pas soumis à la même contrainte. Si vous vous arrangez pour re-consacrer la référence avant la fin du destructeur, Perl appellera de nouveau la méthode DESTROY après la fin de l'appel en cours pour l'objet re-consacré. Cela peut être utilisé pour une délégation propre de la destruction d'objet, ou pour s'assurer que les destructeurs dans la classe de base de votre choix sont appelés. L'appel explicite de DESTROY est aussi possible, mais n'est habituellement pas nécessaire. Ne confondez pas ce qui précède avec la façon dont sont détruits les objets I dans l'objet courant. De tels objets seront libérés et détruits automatiquement en même temps que l'objet courant, pourvu qu'il n'existe pas ailleurs d'autres références pointant vers eux. =head2 Résumé C'est à peu près tout sur le sujet. Il ne vous reste plus qu'à aller acheter un livre sur la méthodologie de conception orientée objet, et vous le frapper sur le front pendant les six prochains mois environ. =head2 Ramasse-Miettes à Deux Phases Dans la plupart des cas, Perl utilise un système de ramasse-miettes simple et rapide basé sur les références. Cela signifie qu'il se produit un déréférencement supplémentaire à un certain niveau, donc si vous n'avez pas compilé votre exécutable de Perl en utilisant l'option C<-O> de votre compilateur C, les performances s'en ressentiront. Si vous I compilé Perl avec C, cela ne comptera probablement pas. Un souci plus sérieux est que la mémoire inaccessible avec un compteur de références différent de zéro ne sera normalement pas libérée. Par conséquent, ceci est une mauvaise idée : { my $a; $a = \$a; } Alors même que $a I s'en aller, elle ne le peut pas. Lorsque vous construirez des structures de données récursives, vous devrez briser vous-même explicitement l'auto-référence si vous ne voulez pas de fuite de mémoire. Par exemple, voici un noeud auto-référent comme ceux qu'on pourrait utiliser dans une structure d'arbre sophistiquée : sub new_node { my $self = shift; my $class = ref($self) || $self; my $node = {}; $node->{LEFT} = $node->{RIGHT} = $node; $node->{DATA} = [ @_ ]; return bless $node => $class; } Si vous créez de tels noeuds, ils ne disparaîtront pas (actuellement) à moins que vous ne brisiez leur auto-référence vous-même (en d'autres termes, cela ne doit pas être considéré comme une caractéristique et vous ne devriez pas vous reposer dessus). Ou presque. Lorsqu'un thread de l'interpréteur se termine finalement (habituellement au moment où votre programme existe), une libération de la mémoire plutôt coûteuse, mais complète par marquage et nettoyage est effectuée, tout ce qui a été alloué par ce thread est détruit. C'est essentiel pour pouvoir supporter Perl comme un langage enfoui et multithread. Par exemple, ce programme montre le ramassage des miettes en deux phases de Perl : #!/usr/bin/perl package Subtle; sub new { my $test; $test = \$test; warn "CREATING " . \$test; return bless \$test; } sub DESTROY { my $self = shift; warn "DESTROYING $self"; } package main; warn "starting program"; { my $a = Subtle->new; my $b = Subtle->new; $$a = 0; # break selfref warn "leaving block"; } warn "just exited block"; warn "time to die..."; exit; Exécuté en tant que F, la sortie suivante est produite : starting program at /tmp/test line 18. CREATING SCALAR(0x8e5b8) at /tmp/test line 7. CREATING SCALAR(0x8e57c) at /tmp/test line 7. leaving block at /tmp/test line 23. DESTROYING Subtle=SCALAR(0x8e5b8) at /tmp/test line 13. just exited block at /tmp/test line 26. time to die... at /tmp/test line 27. DESTROYING Subtle=SCALAR(0x8e57c) during global destruction. Vous avez remarqué le "global destruction" ? C'est le ramasse-miettes du thread en train d'atteindre l'inaccessible. Les objets sont toujours détruits, même lorsque les références normales ne le sont pas. Les objets sont supprimés lors d'une passe distincte avant les références ordinaires juste pour éviter aux destructeurs d'objets d'utiliser des références ayant déjà été elles-mêmes détruites [NDT au relecteur : diff. entre références normales (regular) et ordinaires ???]. Les simples références ne sont supprimées que si le niveau de destruction est supérieur à 0. Vous pouvez tester les plus hauts niveaux de destruction globale en fixant la variable d'environnement PERL_DESTRUCT_LEVEL, si C<-DDEBUGGING> a été utilisée pendant la compilation de perl. Une stratégie plus complète de ramassage des miettes sera implémentée à l'avenir. En attendant, la meilleure solution est de créer une classe de conteneur non-récursif détenant un pointeur vers la structure de données auto-référentielle. Puis, de définir une méthode DESTROY pour la classe de conteneurs qui brise manuellement les circularités dans la structure auto-référentielle. =head1 VOIR AUSSI Un tutoriel plus doux et plus gentil sur la programmation orientée objet en Perl se trouve dans L, L et L. Vous devriez aussi jeter un oeil sur L pour d'autres petits trucs concernant les objets, les pièges, et les astuces, ainsi que sur L pour des guides de style sur la construction de modules et de classes. =head1 TRADUCTION =head2 Version Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.1. Pour en savoir plus concernant ces traductions, consultez L. =head2 Traducteur Roland Trique > =head2 Relecture Philippe de Visme >