.\" Automatically generated by Pod::Man 2.09 (Pod::Simple 3.04) .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PERLTOOT 1" .TH PERLTOOT 1 "2006-03-07" "DocFr" "User Contributed Perl Documentation" .SH "NAME/NOM" .IX Header "NAME/NOM" perltoot \- Tutoriel oriente\*' objet de Tom. .SH "DESCRIPTION" .IX Header "DESCRIPTION" La programmation oriente\*'e objet se vend bien de nos jours. Certains managers pre\*'fe\*'reraient me\*^me s'arre\*^ter de respirer pluto\*^t que de se passer d'objets. Pourquoi cela ? Qu'est\-ce qu'un objet a de si particulier ? Et plus simplement, qu'est\-ce qu'un objet ? .PP Un objet n'est rien de plus qu'une manie\*`re de cacher des comportements complexes derrie\*`re un petit ensemble clair et simple a\*` utiliser. (C'est ce que les professeurs appellent l'abstraction.) De brillant programmeurs qui n'ont rien a\*` faire si ce n'est de re\*'fle\*'chir pendant des semaines entie\*`res sur des proble\*`mes vraiment difficiles cre\*'ent, pour les re\*'soudre, de chouettes objets que des gens normaux peuvent utiliser (C'est ce que les professeurs appellent la re\*'utilisation de programmes). Les utilisateurs (en fait, des programmeurs) peuvent jouer comme ils veulent avec ces objets tout pre\*^ts, mais ils n'ont pas a\*` les ouvrir et a\*` mettre la pagaille a\*` l'inte\*'rieur. C'est comme un e\*'quipement cou\*^teux\ : le contrat spe\*'cifie que la garantie disparai\*^t si vous ouvrez le capot. Donc ne le fai\*^tes pas. .PP Le coeur des objets est la classe, un espace de nommage prote\*'ge\*' et un peu prive\*' contenant donne\*'es et fonctions. Une classe est un ensemble de routines relatives a\*` un me\*^me proble\*`me. Vous pouvez aussi la conside\*'rer comme un type de\*'fini par l'utilisateur. Le me\*'canisme de paquetage de Perl, qui est aussi utilise\*' pour des modules traditionnels, sert pour les modules de classe. Les objets X vivent X dans une classe ce qui signifie qu'ils appartiennent a\*` un paquetage. .PP La plupart du temps, la classe fournit a\*` l'utilisateur de petits trucs. Ces trucs sont des objets. Ils savent a\*` quelle classe ils appartiennent et comment se comporter. Les utilisateurs demandent quelque chose a\*` la classe comme X donne-moi un objet X. Ou ils peuvent demander a\*` l'un de ces objets de faire quelque chose. Demander a\*` une classe de faire quelque chose pour vous consiste a\*` appeler une \fIme\*'thode de la classe\fR. Demander a\*` un objet de faire quelque chose pour vous consiste a\*` appeler une \fIme\*'thode d'objet\fR. Demander soit a\*` une classe (le cas usuel), soit a\*` un objet (parfois) de vous retourner un objet consiste a\*` appeler un \fIconstructeur\fR qui est simplement une sorte de me\*'thode. .PP Bon d'accord, mais en quoi un objet est-il diffe\*'rent de n'importe quel autre type de donne\*'e en Perl ? Qu'est ce qu'un objet \fIre\*'ellement\fR ? Autrement dit, quel est son type de base ? La re\*'ponse a\*` la premie\*`re question est simple. Un objet n'a qu'une seule diffe\*'rence avec n'importe quel autre type de donne\*'e en Perl\ : vous pouvez le de\*'re\*'fe\*'rencer non seulement par une chai\*^ne ou un nombre comme les tables de hachage ou les tableaux, mais aussi par des appels a\*` des routines nomme\*'es. En un mot, les \fIme\*'thodes\fR. .PP La re\*'ponse a\*` la seconde question est que c'est une re\*'fe\*'rence, mais pas n'importe quelle re\*'fe\*'rence. Une re\*'fe\*'rence qui a e\*'te\*' \fIbe\*'nie\fR (blessed en anglais) par une classe particulie\*`re (lire: paquetage). Quel type de re\*'fe\*'rence ? Bon, la re\*'ponse a cette question est un peu moins concre\*`te, parce qu'en Perl, le concepteur de la classe peut utiliser n'importe quel type de re\*'fe\*'rence. Ce peut e\*^tre un scalaire, un tableau ou une table de hachage. Cela peut me\*^me e\*^tre une re\*'fe\*'rence a\*` du code. Mais de par sa flexibilite\*' inhe\*'rente, un objet est dans la plupart des cas une re\*'fe\*'rence a\*` une table de hachage. .SH "Cre\*'er une classe" .IX Header "Cre'er une classe" Avant de cre\*'er une classe, vous devez choisir son nom. Car le nom de la classe (du paquetage) de\*'termine le nom du fichier qui la contiendra exactement comme pour les modules. Ensuite, cette classe devrait fournir un ou plusieurs moyens de cre\*'er des objets. Finalement, elle devrait proposer des me\*'canismes pour permettre a\*` l'utilisateur de ces objets de les manipuler indirectement a\*` distance. .PP Par exemple, cre\*'ons un simple module pour la classe Person. Elle doit e\*^tre stocke\*'e dans le fichier Person.pm. Si elle s'appelait Happy::Person, elle devrait e\*^tre stocke\*'e dans le fichier Happy/Person.pm et son paquetage deviendrait Happy::Person au lieu de Person. (Sur un ordinateur personnel n'utilisant pas Unix ou Plan 9 mais quelque chose comme MacOS ou \s-1VMS\s0, le se\*'parateur de re\*'pertoires peut e\*^tre diffe\*'rent, mais le principe reste le me\*^me.) Ne voyez aucune forme de relation entre les modules en vous basant sur le nom de leur re\*'pertoire. C'est simplement une facilite\*' de regroupement et cela n'a aucun effet sur l'he\*'ritage, l'accessibilite\*' des variables ou quoi que ce soit d'autres. .PP Pour ce module, nous n'utiliserons pas l'Exporter puisque nous construirons une classe bien e\*'leve\*'e qui n'exportera rien du tout. Pour cre\*'er des objets, une classe doit avoir un \fIconstructeur\fR. Un constructeur ne vous renvoie pas seulement un type normal de donne\*'e, mais un objet tout neuf de cette classe. Cela est fait automagiquement par la fonction \fIbless()\fR dont le seul ro\*^le est d'autoriser l'utilisation d'une re\*'fe\*'rence en tant qu'objet. Rappelez-vous\ : e\*^tre un objet ne signifie rien de plus que pouvoir appeler des me\*'thodes a\*` partir de soi\-me\*^me. .PP Bien qu'un constructeur puisse porter le nom que l'on veut, la plupart des programmeurs Perl semble les appeler \fInew()\fR. Par contre \fInew()\fR n'est pas un mot re\*'serve\*' et une classe n'a aucune obligation de proposer une me\*'thode portant ce nom. Quelques programmeurs utilisent aussi une fonction avec le me\*^me nom que la classe du constructeur. .Sh "Repre\*'sentation des objets" .IX Subsection "Repre'sentation des objets" Pour repre\*'senter une structure C ou une classe \*(C+, le me\*'canisme le plus couramment utilise\*' en Perl, et de loin, est une table de hachage anonyme. Parce qu'une table de hachage peut contenir un nombre arbitraire de champs tous accessibles par un nom arbitrairement choisi. .PP Si vous voulez une e\*'mulation simple d'une structure, vous devriez e\*'crire quelque chose comme\ : .PP .Vb 5 \& $rec = { \& name => "Jason", \& age => 23, \& peers => [ "Norbert", "Rhys", "Phineas"], \& }; .Ve .PP Si vous pre\*'fe\*'rez, vous pouvez cre\*'er un peu de diffe\*'rence visuelle en mettant les cle\*'s en majuscules\ : .PP .Vb 5 \& $rec = { \& NAME => "Jason", \& AGE => 23, \& PEERS => [ "Norbert", "Rhys", "Phineas"], \& }; .Ve .PP Et vous utilisez \f(CW\*(C`$rec\->{NAME}\*(C'\fR pour trouver \*(L"Jason\*(R" ou \f(CW\*(C`@{ $rec\->{PEERS} }\*(C'\fR pour obtenir \*(L"Norbert\*(R", \*(L"Rhys\*(R", et \*(L"Phineas\*(R". (Avez\-vous remarque\*' combien de programmeurs de 23 ans semblent s'appeler \*(L"Jason\*(R" ces temps-ci ? :\-) .PP Ce mode\*`le est parfois utilise\*' pour des classes bien que la possibilite\*' offerte a\*` n'importe qui a\*` l'exte\*'rieur de la classe de modifier directement et impune\*'ment les donne\*'es internes (les donne\*'es de l'objet) ne soit pas conside\*'re\*'e comme le summum d'une programmation de qualite\*'. En ge\*'ne\*'ral, un objet devrait apparai\*^tre comme un truc opaque auquel on acce\*`de via des \&\fIme\*'thodes de l'objet\fR. Visuellement, les me\*'thodes permettent de de\*'re\*'fe\*'rencer une re\*'fe\*'rence en utilisant un nom de fonction pluto\*^t que des crochets ou des accolades. .Sh "L'interface d'une classe" .IX Subsection "L'interface d'une classe" Certains langages disposent d'une interface syntaxique formelle vers les me\*'thodes d'une classe. Perl ne posse\*`de rien de tout cela. C'est a\*` vous de lire la documentation de chaque classe. Si vous appelez une me\*'thode non\-de\*'finie pour un objet, Perl ne dira rien, mais votre programme engendrera une exception durant son exe\*'cution. De me\*^me, si vous appelez une me\*'thode qui attend un nombre premier comme argument avec un nombre non-premier a\*` la place, vous ne pouvez espe\*'rer que le compilateur le de\*'tecte. (En fait, vous pouvez toujours espe\*'rer, mais cela n'arrivera pas.) .PP Supposons que l'utilisateur de votre classe Person soit respectueux (quelqu'un qui a lu la documentation expliquant l'interface prescrite). Voici comment il devrait utiliser la classe Person: .PP .Vb 1 \& use Person; \& \& $him = Person\->new(); \& $him\->name("Jason"); \& $him\->age(23); \& $him\->peers( "Norbert", "Rhys", "Phineas" ); \& \& push @All_Recs, $him; # stockage dans un tableau pour plus tard \& \& printf "%s is %d years old.\en", $him\->name, $him\->age; \& print "His peers are: ", join(", ", $him\->peers), "\en"; \& \& printf "Last rec's name is %s\en", $All_Recs[\-1]\->name; .Ve .PP Comme vous pouvez le voir, l'utilisateur de la classe ne sait pas (ou au moins, n'a pas a\*` faire attention) si l'objet a une imple\*'mentation particulie\*`re ou une autre. L'interface vers la classe et ses objets se fait exclusivement via des me\*'thodes et c'est la seule chose avec laquelle l'utilisateur travaille. .Sh "Constructeurs et me\*'thodes d'objet" .IX Subsection "Constructeurs et me'thodes d'objet" Par contre, \fIquelqu'un\fR doit savoir ce qu'il y a dans l'objet. Ce quelqu'un c'est la classe. Elle imple\*'mente les me\*'thodes que le programmeur utilise pour acce\*'der a\*` l'objet. Voici le manie\*`re d'imple\*'menter la classe Person en utilisant l'idiome standard objet\-re\*'fe\*'rence\-vers\-table\-de\-hachage. Nous fabriquons un me\*'thode de classe appele\*'e \fInew()\fR en guise de constructeur et trois me\*'thodes d'objet appele\*'es \fIname()\fR, \fIage()\fR et \fIpeers()\fR pour cacher l'acce\*`s a\*` nos donne\*'es d'objet dans notre table de hachage anonyme. .PP .Vb 2 \& package Person; \& use strict; \& \& ################################################## \& ## le constructeur d'objet (version simpliste) ## \& ################################################## \& sub new { \& my $self = {}; \& $self\->{NAME} = undef; \& $self\->{AGE} = undef; \& $self\->{PEERS} = []; \& bless($self); # voir ci\-dessous \& return $self; \& } \& \& #################################################### \& ## me\*'thodes pour acce\*'der aux donne\*'es d'objet ## \& ## ## \& ## Avec des arguments, elles changent la(les) ## \& ## valeur(s) ## \& ## Sans, elles ne font que la(les) retrouver. ## \& #################################################### \& \& sub name { \& my $self = shift; \& if (@_) { $self\->{NAME} = shift } \& return $self\->{NAME}; \& } \& \& sub age { \& my $self = shift; \& if (@_) { $self\->{AGE} = shift } \& return $self\->{AGE}; \& } \& \& sub peers { \& my $self = shift; \& if (@_) { @{ $self\->{PEERS} } = @_ } \& return @{ $self\->{PEERS} }; \& } \& \& 1; # ainsi le 'require' ou le 'use' re\*'ussi .Ve .PP Nous avons cre\*'e\*' trois me\*'thodes pour acce\*'der aux donne\*'es de l'objet: \fIname()\fR, \&\fIage()\fR et \fIpeers()\fR. Elles sont similaires. Si elles sont appele\*'es avec un argument, elles stockent la nouvelle valeur du champ. Sans argument, elles retournent la valeur contenu dans le champ correspondant, c'est\-a\*`\-dire la valeur indexe\*'e par cette cle\*' dans la table de hachage. .Sh "En pre\*'vision du futur: de meilleurs constructeurs" .IX Subsection "En pre'vision du futur: de meilleurs constructeurs" Bien que pour l'instant vous ne sachiez peut\-e\*^tre pas du tout ce que c'est, il faut faire attention a\*` l'he\*'ritage. (Vous pouvez ne pas vous en occuper pour l'instant et n'y revenir que plus tard.) Pour e\*^tre su\*^r que tout cela fonctionne correctement, vous devez utiliser la fonction \fIbless()\fR sous sa forme a\*` deux arguments. Le second argument est la classe par laquelle la re\*'fe\*'rence doit e\*^tre be\*'nie (blessed). Si vous utilisez notre propre classe comme second argument par de\*'faut pluto\*^t que de retrouver la classe qui vous est passe\*'e, vous rendez votre constructeur inhe\*'ritable. .PP .Vb 9 \& sub new { \& my $class = shift; \& my $self = {}; \& $self\->{NAME} = undef; \& $self\->{AGE} = undef; \& $self\->{PEERS} = []; \& bless ($self, $class); \& return $self; \& } .Ve .PP C'est tout ce qu'il y a a\*` savoir sur les constructeurs. Ces me\*'thodes donnent vie aux objets et retournent a\*` l'utilisateur un truc opaque qui sera utilise\*' plus tard pour appeler des me\*'thodes. .Sh "Destructeurs" .IX Subsection "Destructeurs" Toutes les histoires ont un de\*'but et une fin. Le de\*'but de l'histoire d'un objet est son constructeur qui est explicitement appele\*' a\*` la cre\*'ation de l'objet. Le \fIdestructeur\fR repre\*'sente la fin de l'objet. Il est appele\*' implicitement lorsque l'objet disparai\*^t. Tout code de nettoyage lie\*' a\*` l'objet doit e\*^tre place\*' dans le destructeur qui (en Perl) doit s'appeler \s-1DESTROY\s0. .PP Pourquoi les constructeurs peuvent-ils avoir des noms arbitraires et pas les destructeurs ? Parce qu'un constructeur est appele\*' explicitement ce qui n'est pas le cas du destructeur. La destruction se fait automatiquement via le syste\*`me de ramasse-miettes (\s-1GC\s0) de Perl qui est un \s-1GC\s0 a\*` base de re\*'fe\*'rence souvent rapide, mais parfois un peu indolent. Pour savoir quoi appeler, Perl impose que le destructeur s'appelle \s-1DESTROY\s0. La notion de bon moment pour appeler le destructeur n'est pas tre\*`s bien de\*'finie actuellement en Perl. C'est pourquoi vos destructeurs ne doivent pas de\*'pendre du moment ou\*` ils sont appele\*'s. .PP Pourquoi \s-1DESTROY\s0 est tout en majuscule ? C'est une convention en Perl que toutes les fonctions entie\*`rement en majuscule peuvent e\*^tre appele\*'es automatiquement par Perl. Il en existe d'autres qui sont appele\*'es implicitement comme \s-1BEGIN\s0, \s-1END\s0, \s-1AUTOLOAD\s0, plus tous les me\*'thodes utilise\*'es par les objets tied (cravate\*'s ?) et de\*'crites dans perltie. .PP Dans les langages re\*'ellement oriente\*'s objets, l'utilisateur n'a pas a\*` se pre\*'occuper d'appeler le destructeur. Cela arrive automagiquement quand il faut. Dans les langages de bas niveau sans ramasse-miettes du tout, il n'y a aucun moyen d'automatiser cela. C'est donc au programmeur d'appeler explicitement le destructeur pour nettoyer la me\*'moire en croisant les doigts pour que ce soit le bon moment. Au contraire de \*(C+, un destructeur est tre\*`s rarement ne\*'cessaire en Perl et me\*^me quand il l'est, il n'est pas ne\*'cessaire de l'appeler explicitement. Dans le cas de notre classe Person, nous n'avons pas besoin de destructeur, car Perl s'occupe tout seul de pas mal de choses, comme la libe\*'ration de la me\*'moire. .PP La seule situation ou\*` le ramasse-miettes de Perl e\*'choue, c'est en pre\*'sence de re\*'fe\*'rences circulaires comme celle-ci\ : .PP .Vb 1 \& $this\->{WHATEVER} = $this; .Ve .PP Dans ce cas, vous devez de\*'truire manuellement la re\*'fe\*'rence circulaire si vous voulez que votre programme n'ait pas de fuites me\*'moire. C'est l'ide\*'al. Par contre, assurez-vous que lorsque votre programme se termine, tous les destructeurs de ces objets sont appele\*'s. Ainsi vous e\*^tes su\*^r qu'un objet sera de\*'truit proprement, sauf dans le cas ou\*` votre programme ne se termine jamais. (Si vous faites tourner Perl a\*` l'inte\*'rieur d'une autre application, cette passe comple\*`te du ramasse-miettes peut arriver plus souvent \*(-- par exemple, a\*` la fin de chaque fil d'exe\*'cution.) .Sh "Autres me\*'thodes d'objets" .IX Subsection "Autres me'thodes d'objets" Les me\*'thodes dont nous avons parle\*' jusqu'a\*` pre\*'sent sont soit des constructeurs, soit de simples me\*'thodes d'acce\*`s aux donne\*'es stocke\*'es dans l'objet. Cela ressemble un peu aux donne\*'es membres des objets du monde \*(C+, sauf que dans ce cas les \*(L"e\*'trangers\*(R" n'y acce\*`dent pas comme des donne\*'es. A\*` la place, ils doivent y acce\*'der indirectement via des me\*'thodes. Re\*`gle importante : en Perl, l'acce\*`s aux donne\*'es d'un objet ne devrait se faire \fIque\fR par l'interme\*'diaire des me\*'thodes. .PP Perl n'impose aucune restriction sur qui utilise quelles me\*'thodes. La distinction publique/prive\*'e n'est pas syntaxique. C'est une convention. (Sauf si vous utilisez le module Alias de\*'crit plus bas dans \*(L"Les donne\*'es membres comme des variables\*(R".) De temps en temps, vous verrez des me\*'thodes dont le nom commence ou finit par un ou deux caracte\*`res de soulignement. C'est une convention signifiant que ces me\*'thodes sont prive\*'es et utilisables uniquement par cette classe, et e\*'ventuellement des classes proches ou ses sous\-classes. Mais Perl ne fait pas respecter cette distinction. C'est au programmeur de bien se comporter. .PP Il n'y a aucune raison de limiter l'usage des me\*'thodes a\*` l'acce\*`s aux donne\*'es de l'objet. Une me\*'thode peut faire tout ce qu'on veut. Le point cle\*' est de savoir si elle est appele\*'e comme me\*'thode de classe ou comme me\*'thode d'objet. Supposons que vous vouliez e\*'crire une me\*'thode objet qui fasse plus que donner l'acce\*`s en lecture et/ou e\*'criture a\*` un champ particulier\ : .PP .Vb 5 \& sub exclaim { \& my $self = shift; \& return sprintf "Hello, Je suis %s, j'ai %d ans, je travaille avec %s", \& $self\->{NAME}, $self\->{AGE}, join(", ", @{$self\->{PEERS}}); \& } .Ve .PP ou quelque chose comme c\*,a\ : .PP .Vb 4 \& sub joyeux_anniversaire { \& my $self = shift; \& return ++$self\->{AGE}; \& } .Ve .PP Certains diront qu'il vaut mieux les e\*'crire comme c\*,a\ : .PP .Vb 5 \& sub exclaim { \& my $self = shift; \& return sprintf "Hello, Je suis %s, j'ai %d ans, je travaille avec %s", \& $self\->name, $self\->age, join(", ", $self\->peers); \& } \& \& sub joyeux_anniversaire { \& my $self = shift; \& return $self\->age( $self\->age() + 1 ); \& } .Ve .PP Mais comme ces me\*'thodes sont toutes exe\*'cute\*'es dans la classe elle\-me\*^me, ce n'est pas critique. Le choix est une histoire de compromis. L'utilisation directe de la table de hachage est plus rapide (d'un ordre de grandeur pour e\*^tre pre\*'cis). Mais l'usage des me\*'thodes (c'est a\*` dire l'interface externe) prote\*`ge non seulement l'utilisateur de votre classe, mais aussi vous\-me\*^me des e\*'ventuels changements dans la repre\*'sentation interne de vos donne\*'es objet. .SH "Donne\*'es de classe" .IX Header "Donne'es de classe" Que dire des donne\*'es de classe, les donne\*'es communes a\*` tous les objets de la classe ? A\*` quoi peuvent-elles vous servir ? Supposons que dans votre classe Person, vous vouliez garder une trace du nombre total de personnes. Comment imple\*'menter cela ? .PP Vous \fIpourriez\fR en faire une variable globale appele\*'e \f(CW$Person::Census\fR. Mais la seule justification valable pour agir ainsi serait de donner au gens la possibilite\*' d'acce\*'der directement a\*` votre donne\*'e de classe. Ils pourraient utiliser \f(CW$Person::Census\fR pour faire ce qu'ils veulent. Peut\-e\*^tre est-ce ce qu'il vous faut. Vous pourriez me\*^me en faire une variable exporte\*'e. Pour e\*^tre exportable, une variable doit e\*^tre globale (au paquetage). Si c'e\*'tait un module traditionnel pluto\*^t qu'un module oriente\*' objet, c'est comme cela qu'il faudrait faire. .PP Bien qu'e\*'tant l'approche utilise\*'e dans la plupart des modules traditionnels, cette manie\*`re de faire est conside\*'re\*'e comme mauvaise dans une approche objet. Dans un module objet, vous devriez pouvoir se\*'parer l'interface de l'imple\*'mentation. Il faut donc proposer des me\*'thodes de classe pour acce\*'der aux donne\*'es de la classe comme le font les me\*'thodes objets pour acce\*'der aux donne\*'es des objets. .PP Donc, vous \fIpourriez\fR garder \f(CW$Census\fR comme une variable globale en espe\*'rant que les autres respecteront le contrat de modularite\*' en n'acce\*'dant pas directement a\*` son imple\*'mentation. Vous pourriez me\*^me e\*^tre plus ruse\*' (voire retors) et faire de \f(CW$Census\fR un objet X tied X comme de\*'crit dans perltie et donc intercepter tous les acce\*`s. .PP Le plus souvent, il vous suffira de de\*'clarer votre donne\*'e de classe avec une porte\*'e lexicale locale au fichier. Pour cela, il vous suffit de mettre la ligne suivante au de\*'but de votre fichier\ : .PP .Vb 1 \& my $Census = 0; .Ve .PP Normalement, la porte\*'e d'une variable \fImy()\fR se termine en me\*^me temps que le bloc dans lequel elle est de\*'clare\*'e (dans notre cas ce serait l'ensemble du fichier requis (par \fIrequire()\fR) ou utilise\*' (par \fIuse()\fR)), mais en fait le me\*'canisme de gestion des variables lexicales imple\*'mente\*' par Perl garantit que la variable ne sera pas de\*'salloue\*'e et restera accessible a\*` toutes les fonctions ayant la me\*^me porte\*'e. Par contre, cela ne fonctionne pas avec les variables globales auxquelles on donne une valeur temporaire via \fIlocal()\fR. .PP Inde\*'pendamment de la me\*'thode choisie ($Census comme variable globale du paquetage ou avec une porte\*'e lexicale locale au fichier), vous devrez modifier le constructeur $\fIPerson::new()\fR de la manie\*`re suivante\ : .PP .Vb 10 \& sub new { \& my $class = shift; \& my $self = {}; \& $Census++; \& $self\->{NAME} = undef; \& $self\->{AGE} = undef; \& $self\->{PEERS} = []; \& bless ($self, $class); \& return $self; \& } \& \& sub population { \& return $Census; \& } .Ve .PP Ceci e\*'tant fait, nous avons maintenant besoin d'un destructeur afin de de\*'cre\*'menter \f(CW$Census\fR lorsqu'un objet Person est de\*'truit. Voici ce destructeur\ : .PP .Vb 1 \& sub DESTROY { \-\-$Census } .Ve .PP Remarquez qu'il n'y a pas de me\*'moire a\*` de\*'sallouer dans le destructeur ! C'est Perl qui s'en occupe pour vous tout seul. .PP Pour faire tout cela vous pouvez aussi utiliser le module Class::Data::Inheritable disponible sur \s-1CPAN\s0. .Sh "Acce\*`s aux donne\*'es de classe" .IX Subsection "Acce`s aux donne'es de classe" Il s'ave\*`re que ce n'est pas vraiment une bonne chose que de manipuler les donne\*'es de classe. Un bonne re\*`gle est\ : \fIvous ne devriez jamais re\*'fe\*'rencer directement des donne\*'es de classe directement dans une me\*'thode objet\fR. Car, sinon, vous ne construisez pas une classe he\*'ritable. L'objet doit e\*^tre le point de rendez-vous pour toutes les ope\*'rations, et en particulier depuis les me\*'thodes objets. Les donne\*'es globales (les donne\*'es de classe) peuvent e\*^tre dans le mauvais paquetage du point de vue des classes he\*'rite\*'es. En Perl, les me\*'thodes s'exe\*'cutent dans le contexte de la classe ou\*` elles sont de\*'finies et \&\fIpas\fR dans celui de l'objet qui les a appele\*'es. Par conse\*'quent, la visibilite\*' des variables globales d'un paquetage dans les me\*'thodes n'est pas lie\*'e a\*` l'he\*'ritage. .PP Ok, supposons qu'une autre classe emprunte (en fait he\*'rite de) la me\*'thode \&\s-1DESTROY\s0 telle qu'elle est de\*'finie pre\*'ce\*'demment. Lorsque ses objets sont de\*'truits, c'est la variable originale \f(CW$Census\fR qui est modifie\*'e et non pas celle qui est dans l'espace de noms du paquetage de la nouvelle classe. La plupart du temps, cela ne correspond pas a\*` ce que vous vouliez. .PP Bon, voici comment y reme\*'dier. Nous allons stocker une re\*'fe\*'rence a\*` la variable dans la valeur associe\*'e a\*` la cle\*' \*(L"_CENSUS\*(R" de la table de hachage de l'objet. Pourquoi un caracte\*`re de soulignement au de\*'but ? Principalement parce que cela e\*'voque une notion magique a\*` un programmeur C. C'est en fait un moyen mne\*'motechnique pour nous souvenir que ce champ est spe\*'cial et qu'il ne doit pas e\*^tre utilise\*' comme une donne\*'e publique telle que \s-1NAME\s0, \s-1AGE\s0 ou \s-1PEERS\s0. (En raison de l'utilisation du pragma strict, dans les versions ante\*'rieures a\*` la 5.004, nous devons entourer de guillemets le nom du champ.) .PP .Vb 12 \& sub new { \& my $class = shift; \& my $self = {}; \& $self\->{NAME} = undef; \& $self\->{AGE} = undef; \& $self\->{PEERS} = []; \& # donne\*'es "prive\*'es" \& $self\->{"_CENSUS"} = \e$Census; \& bless ($self, $class); \& ++ ${ $self\->{"_CENSUS"} }; \& return $self; \& } \& \& sub population { \& my $self = shift; \& if (ref $self) { \& return ${ $self\->{"_CENSUS"} }; \& } else { \& return $Census; \& } \& } \& \& sub DESTROY { \& my $self = shift; \& \-\- ${ $self\->{"_CENSUS"} }; \& } .Ve .Sh "Me\*'thodes de de\*'boggage" .IX Subsection "Me'thodes de de'boggage" Une classe propose souvent un me\*'canisme de de\*'boggage. Par exemple, vous pourriez vouloir voir quand les objets sont cre\*'e\*'s et de\*'truits. Pour cela, il vous faut ajouter un variable de de\*'boggage de porte\*'e lexicale limite\*'e au fichier. Nous utiliserons aussi le module standard Carp pour e\*'mettre nos avertissements (warnings) et nos messages d'erreur. Ainsi ces messages s'afficheront avec le nom et le nume\*'ro de la ligne du fichier de l'utilisateur pluto\*^t qu'avec ceux de notre fichier. Si nous les voulons de notre point de vue, il nous suffit d'utiliser respectivement \fIdie()\fR et \fIwarn()\fR pluto\*^t que \&\fIcroak()\fR et \fIcarp()\fR .PP .Vb 2 \& use Carp; \& my $Debugging = 0; .Ve .PP Ajoutons maintenant une nouvelle me\*'thode de classe pour acce\*'der a\*` cette variable. .PP .Vb 6 \& sub debug { \& my $class = shift; \& if (ref $class) { confess "Class method called as object method" } \& unless (@_ == 1) { confess "usage: CLASSNAME\->debug(level)" } \& $Debugging = shift; \& } .Ve .PP Modifions \s-1DESTROY\s0 pour afficher un petit quelque chose lorsqu'un objet disparai\*^t: .PP .Vb 5 \& sub DESTROY { \& my $self = shift; \& if ($Debugging) { carp "Destroying $self " . $self\->name } \& \-\- ${ $self\->{"_CENSUS"} }; \& } .Ve .PP Il est concevable d'avoir un me\*'canisme de de\*'boggage par objet. Afin de pouvoir utiliser les deux appels suivants\ : .PP .Vb 2 \& Person\->debug(1); # toute la classe \& $him\->debug(1); # juste un objet .Ve .PP Nous avons donc besoin de rendre \*(L"bimodale\*(R" notre me\*'thode debug afin qu'elle fonctionne a\*` la fois sur la classe \fIet\fR sur les objets. Modifions donc \&\fIdebug()\fR et \s-1DESTROY\s0 comme suit\ : .PP .Vb 10 \& sub debug { \& my $self = shift; \& confess "usage: thing\->debug(level)" unless @_ == 1; \& my $level = shift; \& if (ref($self)) { \& $self\->{"_DEBUG"} = $level; # juste moi\-me\*^me \& } else { \& $Debugging = $level; # toute la classe \& } \& } \& \& sub DESTROY { \& my $self = shift; \& if ($Debugging || $self\->{"_DEBUG"}) { \& carp "Destroying $self " . $self\->name; \& } \& \-\- ${ $self\->{"_CENSUS"} }; \& } .Ve .PP Que se passe-t-il si une classe de\*'rive\*'e (que nous appellerons Employee) he\*'rite de ces me\*'thodes depuis la classe de base Person ? Eh bien, \&\f(CW\*(C`Employee\->debug()\*(C'\fR, lorsqu'elle est appele\*'e en tant que me\*'thode de classe, modifie \f(CW$Person::Debugging\fR et non \f(CW$Employee::Debugging\fR. .Sh "Destructeurs de classes" .IX Subsection "Destructeurs de classes" Le destructeur d'objet traite la disparition de chaque objet. Mais parfois, vous devez faire un peu de nettoyage lorsque toute la classe disparai\*^t. Ce qui n'arrive actuellement qu'a\*` la fin du programme. Pour faire un \fIdestructeur de classe\fR, cre\*'ez une fonction \s-1END\s0 dans le paquetage de la classe. Elle fonctionne exactement comme la fonction \s-1END\s0 des modules traditionnels. Ce qui signifie qu'elle est appele\*'e lorsque votre programme se termine, a\*` moins qu'il n'effectue un 'exec' ou qu'il meurt sur un signal non capte\*'. Par exemple\ : .PP .Vb 5 \& sub END { \& if ($Debugging) { \& print "All persons are going away now.\en"; \& } \& } .Ve .PP Quand le programme se termine, tous les destructeurs de classes (les fonctions \&\s-1END\s0) sont exe\*'cute\*'s dans l'ordre inverse de leur chargement (\s-1LIFO\s0). .Sh "La documentation de l'interface" .IX Subsection "La documentation de l'interface" Jusqu'ici nous n'avons exhibe\*' que \fIl'imple\*'mentation\fR de la classe Person. Son \&\fIinterface\fR doit e\*^tre sa documentation. Habituellement cela signifie d'ajouter du pod (\*(L"plain old documentation\*(R") dans le me\*^me fichier. Dans le cas de notre exemple Person, nous devons placer la documentation suivante quelque part dans le fichier Person.pm. Bien que cela ressemble a\*` du code, ce n'en est pas. C'est de la documentation inte\*'gre\*'e utilisable par des programmes comme pod2man, pod2html ou pod2text. Le compilateur Perl ne tient pas compte des parties pod. A\*` l'inverse, les traducteurs pod ne tiennent pas compte des parties de code. Voici un exemple de pod de\*'crivant notre interface\ : .PP .Vb 1 \& =head1 NAME \& \& Person \- classe pour imple\*'menter des gens \& \& =head1 SYNOPSIS \& \& use Person; \& \& ###################### \& # me\*'thodes de classe # \& ###################### \& $ob = Person\->new; \& $count = Person\->population; \& \& ########################################### \& # me\*'thodes d'acce\*`s aux donne\*'es d'objets # \& ########################################### \& \& ### acce\*`s en lecture ### \& $who = $ob\->name; \& $years = $ob\->age; \& @pals = $ob\->peers; \& \& ### acce\*`s en e\*'criture ### \& $ob\->name("Jason"); \& $ob\->age(23); \& $ob\->peers( "Norbert", "Rhys", "Phineas" ); \& \& ############################ \& # autres me\*'thodes d'objets # \& ############################ \& \& $phrase = $ob\->exclaim; \& $ob\->joyeux_anniversaire; \& \& =head1 DESCRIPTION \& \& La classe Person permet de blah, blah, blah... .Ve .PP C'est donc tout ce qui concerne l'interface et non pas l'imple\*'mentation. Un programmeur qui ouvre le module pour jouer avec toutes les astuces d'imple\*'mentation qui sont cache\*'es derrie\*`re le contrat d'interface rompt la garantie et vous n'avez pas a\*` vous pre\*'occuper de son sort. .SH "Agre\*'gation" .IX Header "Agre'gation" Supposons que, plus tard, vous vouliez modifier la classe pour avoir une meilleure gestion des noms. Par exemple, en ge\*'rant le pre\*'nom, le nom de famille, mais aussi les surnoms et les titres. Si les utilisateurs de votre classe Person y acce\*`dent proprement via l'interface documente\*'e, vous pouvez alors changer sans risque l'imple\*'mentation sous\-jacente. S'ils ne l'ont pas fait, ils ont tout perdu, mais c'est leur faute puisqu'en rompant le contrat, ils perdent la garantie. .PP Nous allons donc cre\*'er une nouvelle classe appele\*'e Fullname. A\*` quoi ressemblera cette classe Fullname ? Pour pouvoir re\*'pondre, nous devons d'abord examiner comment nous comptons l'utiliser. Par exemple: .PP .Vb 7 \& $him = Person\->new(); \& $him\->fullname\->title("St"); \& $him\->fullname\->christian("Thomas"); \& $him\->fullname\->surname("Aquinas"); \& $him\->fullname\->nickname("Tommy"); \& printf "His normal name is %s\en", $him\->name; \& printf "But his real name is %s\en", $him\->fullname\->as_string; .Ve .PP Ok. Changeons \fIPerson::new()\fR pour qu'il accepte un champ fullname\ : .PP .Vb 11 \& sub new { \& my $class = shift; \& my $self = {}; \& $self\->{FULLNAME} = Fullname\->new(); \& $self\->{AGE} = undef; \& $self\->{PEERS} = []; \& $self\->{"_CENSUS"} = \e$Census; \& bless ($self, $class); \& ++ ${ $self\->{"_CENSUS"} }; \& return $self; \& } \& \& sub fullname { \& my $self = shift; \& return $self\->{FULLNAME}; \& } .Ve .PP Puis, pour accepter le vieux code, de\*'finissons \fIPerson::name()\fR de la manie\*`re suivante\ : .PP .Vb 5 \& sub name { \& my $self = shift; \& return $self\->{FULLNAME}\->nickname(@_) \& || $self\->{FULLNAME}\->christian(@_); \& } .Ve .PP Voici maintenant la classe Fullname. La\*` encore, nous utilisons une table de hachage pour stocker les donne\*'es associe\*'es a\*` des me\*'thodes avec le nom qui va bien pour y acce\*'der\ : .PP .Vb 2 \& package Fullname; \& use strict; \& \& sub new { \& my $class = shift; \& my $self = { \& TITLE => undef, \& CHRISTIAN => undef, \& SURNAME => undef, \& NICK => undef, \& }; \& bless ($self, $class); \& return $self; \& } \& \& sub christian { \& my $self = shift; \& if (@_) { $self\->{CHRISTIAN} = shift } \& return $self\->{CHRISTIAN}; \& } \& \& sub surname { \& my $self = shift; \& if (@_) { $self\->{SURNAME} = shift } \& return $self\->{SURNAME}; \& } \& \& sub nickname { \& my $self = shift; \& if (@_) { $self\->{NICK} = shift } \& return $self\->{NICK}; \& } \& \& sub title { \& my $self = shift; \& if (@_) { $self\->{TITLE} = shift } \& return $self\->{TITLE}; \& } \& \& sub as_string { \& my $self = shift; \& my $name = join(" ", @$self{'CHRISTIAN', 'SURNAME'}); \& if ($self\->{TITLE}) { \& $name = $self\->{TITLE} . " " . $name; \& } \& return $name; \& } \& \& 1; .Ve .PP Pour finir, voici un programme de test\ : .PP .Vb 4 \& #!/usr/bin/perl \-w \& use strict; \& use Person; \& sub END { show_census() } \& \& sub show_census () { \& printf "Current population: %d\en", Person\->population; \& } \& \& Person\->debug(1); \& \& show_census(); \& \& my $him = Person\->new(); \& \& $him\->fullname\->christian("Thomas"); \& $him\->fullname\->surname("Aquinas"); \& $him\->fullname\->nickname("Tommy"); \& $him\->fullname\->title("St"); \& $him\->age(1); \& \& printf "%s is really %s.\en", $him\->name, $him\->fullname\->as_string; \& printf "%s's age: %d.\en", $him\->name, $him\->age; \& $him\->happy_birthday; \& printf "%s's age: %d.\en", $him\->name, $him\->age; \& \& show_census(); .Ve .SH "He\*'ritage" .IX Header "He'ritage" Tous les syste\*`mes de programmation oriente\*' objets incluent sous une forme ou une autre la notion d'he\*'ritage. L'he\*'ritage permet a\*` une classe d'englober une autre classe afin d'e\*'viter de re\*'\-e\*'crire la me\*^me chose plusieurs fois. C'est en rapport avec la re\*'utilisation logicielle et donc avec la paresse qui est, comme chacun sait, la vertu principale d'un programmeur. (Le me\*'canisme d'import/export des modules traditionnels est aussi une forme de re\*'utilisation de code, mais beaucoup plus simple que le vrai he\*'ritage qu'on trouve dans les modules objets.) .PP Parfois la syntaxe de l'he\*'ritage est construite au coeur du langage, mais ce n'est pas toujours le cas. Perl n'a aucune syntaxe spe\*'ciale pour spe\*'cifier la classe (ou les classes) dont on he\*'rite. A\*` la place, tout est fait purement se\*'mantiquement. Chaque paquetage peut contenir une variable appele\*'e \&\f(CW@ISA\fR qui pilote l'he\*'ritage (des me\*'thodes). Si vous essayez d'appeler une me\*'thode d'un objet ou d'une classe et que cette me\*'thode n'est pas trouve\*'e dans le paquetage de l'objet, Perl trouve dans \f(CW@ISA\fR le nom d'autres paquetages ou\*` chercher la me\*'thode manquante. .PP Comme les variables spe\*'ciales de paquetages reconnues par Exporter (telles que \&\f(CW@EXPORT\fR, \f(CW@EXPORT_OK\fR, \f(CW@EXPORT_FAIL\fR, \f(CW%EXPORT_TAGS\fR, et \f(CW$VERSION\fR), le tableau \f(CW@ISA\fR \&\fIdoit\fR e\*^tre une variable de porte\*'e globale au paquetage et non une variable de porte\*'e lexicale limite\*'e au fichier et cre\*'e\*'e par \fImy()\fR. La plupart des classes n'ont qu'un seul nom dans leur tableau \f(CW@ISA\fR. C'est ce que nous appellerons de l'\*(L"He\*'ritage Simple\*(R" ou \s-1HS\s0 pour faire court. .PP Examinons la classe suivante\ : .PP .Vb 4 \& package Employee; \& use Person; \& @ISA = ("Person"); \& 1; .Ve .PP Ce n'est pas grand chose, hein ? Tout ce que c\*,a fait c'est de charger une autre classe et d'indiquer qu'on he\*'rite des me\*'thodes de cette autre classe, si ne\*'cessaire. Nous n'avons spe\*'cifie\*' aucune de ses propres me\*'thodes. Nous obtenons donc un Employee qui se comporte exactement comme une Person. .PP Une telle classe vide est appele\*'e une \*(L"sous\-classe vide de test\*(R". C'est une classe qui ne fait rien si ce n'est d'he\*'riter d'une classe de base. Si la classe originale est correctement conc\*,ue, alors la nouvelle classe de\*'rive\*'e peut e\*^tre utilise\*'e en remplacement de celle d'origine. Cela veut dire que vous pouvez e\*'crire un programme comme celui-ci\ : .PP .Vb 5 \& use Employee; \& my $empl = Employee\->new(); \& $empl\->name("Jason"); \& $empl\->age(23); \& printf "%s is age %d.\en", $empl\->name, $empl\->age; .Ve .PP Par correctement conc\*,ue, nous entendons toujours utiliser \fIbless()\fR dans sa forme a\*` deux arguments, e\*'viter l'acce\*`s direct aux donne\*'es globales et ne rien exporter. En regardant la fonction \fIPerson::new()\fR que nous avons de\*'finie pre\*'ce\*'demment, vous pourrez ve\*'rifier tout cela. Quelques donne\*'es du paquetage sont utilise\*'es dans le constructeur, mais leur re\*'fe\*'rence est stocke\*'e dans l'objet lui\-me\*^me et toutes les autres me\*'thodes acce\*`dent a\*` ces donne\*'es via ces re\*'fe\*'rences. C\*,a doit donc fonctionner. .PP Qu'entendons\-nous par la fonction \fIPerson::new()\fR, \*(-- n'est\-ce pas une me\*'thode ? En principe, oui. Une me\*'thode est simplement une fonction qui attend comme premier parame\*`tre soit un nom de classe (de paquetage), soit un objet (une re\*'fe\*'rence be\*'nie). \fIPerson::new()\fR est la fonction que les deux me\*'thodes \&\f(CW\*(C`Person\->new()\*(C'\fR et \f(CW\*(C`Employee\->new()\*(C'\fR appellent. Bien qu'un appel a\*` une me\*'thode ressemble a\*` un appel de fonction, il faut bien comprendre que ce n'est pas exactement la me\*^me chose. Si vous les conside\*'rez comme identiques, vous vous retrouverez tre\*`s rapidement avec des programmes comple\*`tement boggue\*'s. Tout d'abord, les conventions d'appel sous-jacentes sont diffe\*'rentes: l'appel a\*` une me\*'thode ajoute implicitement un argument supple\*'mentaire. Ensuite, les appels de fonctions n'utilisent pas les me\*'canismes d'he\*'ritage mis en oeuvre pour les me\*'thodes. .PP .Vb 4 \& Appel de me\*'thode Appel de fonction re\*'sultant \& \-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\- \& Person\->new() Person::new("Person") \& Employee\->new() Person::new("Employee") .Ve .PP Donc, n'utilisez pas un appel de fonction a\*` la place d'un appel de me\*'thode. .PP Si un Employee est juste une Person, cela n'est pas tre\*`s utile. Ajoutons donc quelques me\*'thodes. Nous allons ajouter a\*` nos Employee des donne\*'es d'objet pour leur salaire (salary), leur identification (\s-1ID\s0 number) et leur date d'embauche (start date). .PP Si vous e\*^tes fatigue\*' de cre\*'er ces me\*'thodes d'acce\*`s toutes ba\*^ties sur le me\*^me principe, ne de\*'sespe\*'rez pas. Plus tard, nous de\*'crirons diffe\*'rentes techniques pour automatiser cela. .PP .Vb 5 \& sub salary { \& my $self = shift; \& if (@_) { $self\->{SALARY} = shift } \& return $self\->{SALARY}; \& } \& \& sub id_number { \& my $self = shift; \& if (@_) { $self\->{ID} = shift } \& return $self\->{ID}; \& } \& \& sub start_date { \& my $self = shift; \& if (@_) { $self\->{START_DATE} = shift } \& return $self\->{START_DATE}; \& } .Ve .Sh "Polymorphisme" .IX Subsection "Polymorphisme" Qu'arrive\-t\-il lorsqu'une classe de\*'rive\*'e et sa classe de base de\*'finissent toutes deux la me\*^me me\*'thode ? La me\*'thode utilise\*'e est la version de la classe de\*'rive\*'e. Par exemple, supposons que nous voulions que la me\*'thode \fIpeers()\fR agisse diffe\*'remment lorsque nous l'appelons pour un Employee. Au lieu de renvoyer simplement la liste des peers, nous aimerions obtenir une autre chai\*^ne. De telle sorte que\ : .PP .Vb 2 \& $empl\->peers("Peter", "Paul", "Mary"); \& printf "His peers are: %s\en", join(", ", $empl\->peers); .Ve .PP produira\ : .PP .Vb 1 \& His peers are: PEON=PETER, PEON=PAUL, PEON=MARY .Ve .PP Pour obtenir ce re\*'sultat, il faut ajouter la de\*'finition suivante dans le fichier Employee.pm\ : .PP .Vb 5 \& sub peers { \& my $self = shift; \& if (@_) { @{ $self\->{PEERS} } = @_ } \& return map { "PEON=\eU$_" } @{ $self\->{PEERS} }; \& } .Ve .PP Nous venons d'illustrer le concept de \fIpolymorphisme\fR. Nous avons re\*'utilise\*' la forme et le comportement d'un objet existant, puis nous l'avons modifie\*' pour l'adapter a\*` nos besoins. C'est une forme de Paresse. (E\*^tre polymorphe c'est aussi ce qui vous arrive quand un magicien de\*'cide que vous seriez mieux sous la forme d'une grenouille.) .PP Supposons maintenant que nous voulions un appel de me\*'thode qui de\*'clenche a\*` la fois la version de la classe de\*'rive\*'e (aussi appele\*'e sous\-classe) et la version de la classe de base (aussi appele\*'e super\-classe). En pratique, c'est le cas des constructeurs et des destructeurs et probablement celui de la me\*'thode \&\fIdebug()\fR dont nous avons parle\*' pre\*'ce\*'demment. .PP Pour cela, ajoutons ce qui suit dans Employee.pm\ : .PP .Vb 2 \& use Carp; \& my $Debugging = 0; \& \& sub debug { \& my $self = shift; \& confess "usage: thing\->debug(level)" unless @_ == 1; \& my $level = shift; \& if (ref($self)) { \& $self\->{"_DEBUG"} = $level; \& } else { \& $Debugging = $level; # toute la classe \& } \& Person::debug($self, $Debugging); # a\*` ne pas faire ! \& } .Ve .PP Comme vous pouvez le constater nous appelons directement la fonction \fIdebug()\fR du paquetage Person. Mais cela est beaucoup trop spe\*'cifique pour e\*^tre une bonne conception. En effet, que se passe-t-il si Person n'a pas de fonction \&\fIdebug()\fR, mais en he\*'rite d'ailleurs ? Il aurait e\*'te\*' pre\*'fe\*'rable de dire\ : .PP .Vb 1 \& Person\->debug($Debugging); .Ve .PP Mais me\*^me cela est encore de trop bas niveau. Il vaudrait mieux dire\ : .PP .Vb 1 \& $self\->Person::debug($Debugging); .Ve .PP C'est une dro\*^le de manie\*`re pour demander de commencer la recherche de la me\*'thode \fIdebug()\fR a\*` partir de la classe Person. Cette strate\*'gie est plus souvent utilise\*'e pour des me\*'thodes d'objets que pour des me\*'thodes de classe. .PP Cela laisse encore a\*` de\*'sirer. Nous avons code\*' en dur le nom de notre super\-classe. Ce qui n'est pas bon, en particulier si nous changeons la classe dont on he\*'rite ou si on en ajoute d'autres. Heureusement, la pseudo-classe \&\s-1SUPER\s0 a e\*'te\*' cre\*'e\*'e pour nous sauver. .PP .Vb 1 \& $self\->SUPER::debug($Debugging); .Ve .PP Comme c\*,a, la recherche s'effectue dans tous les classes du tableau \&\f(CW@ISA\fR. Cela n'a un sens \fIque\fR lors de l'appel d'une me\*'thode. N'essayez pas d'utiliser \s-1SUPER\s0 dans un autre contexte parce qu'elle n'existe que dans le cas d'appel a\*` des me\*'thodes rede\*'finies. Notez aussi que \&\f(CW\*(C`SUPER\*(C'\fR se re\*'fe\*'re a\*` la super-classe du package courant et \fInon\fR a\*` celle de \f(CW$self\fR. .PP Tout cela est devenu un peu complique\*' maintenant. Avons-nous tout fait comme il fallait ? Comme pre\*'ce\*'demment, pour ve\*'rifier que notre classe est correctement conc\*,ue, nous allons utiliser une sous-classe vide de test. Puisque nous avons de\*'ja\*` une classe Employee que nous voulons tester, notre classe de test sera de\*'rive\*'e de Employee. En voici une\ : .PP .Vb 3 \& package Boss; \& use Employee; # :\-) \& @ISA = qw(Employee); .Ve .PP Et voici le programme de test\ : .PP .Vb 4 \& #!/usr/bin/perl \-w \& use strict; \& use Boss; \& Boss\->debug(1); \& \& my $boss = Boss\->new(); \& \& $boss\->fullname\->title("Don"); \& $boss\->fullname\->surname("Pichon Alvarez"); \& $boss\->fullname\->christian("Federico Jesus"); \& $boss\->fullname\->nickname("Fred"); \& \& $boss\->age(47); \& $boss\->peers("Frank", "Felipe", "Faust"); \& \& printf "%s is age %d.\en", $boss\->fullname\->as_string, $boss\->age; \& printf "His peers are: %s\en", join(", ", $boss\->peers); .Ve .PP Son exe\*'cution nous montre que tout marche bien. Si vous voulez afficher votre objet sous une forme lisible comme le fait la commande 'x' du debogguer, vous pouvez utiliser le module Data::Dumper disponible au \s-1CPAN\s0\ : .PP .Vb 3 \& use Data::Dumper; \& print "Here's the boss:\en"; \& print Dumper($boss); .Ve .PP Ce qui devrait vous afficher quelque chose comme\ : .PP .Vb 10 \& Here's the boss: \& $VAR1 = bless( { \& _CENSUS => \e1, \& FULLNAME => bless( { \& TITLE => 'Don', \& SURNAME => 'Pichon Alvarez', \& NICK => 'Fred', \& CHRISTIAN => 'Federico Jesus' \& }, 'Fullname' ), \& AGE => 47, \& PEERS => [ \& 'Frank', \& 'Felipe', \& 'Faust' \& ] \& }, 'Boss' ); .Ve .PP Heu... Il manque quelque chose. Ou\*` sont les champs salary, start_date et \s-1ID\s0 ? Nous ne leur avons jamais donne\*' de valeur, me\*^me pas undef, donc ils n'apparaissent pas dans les cle\*'s de la table de hachage. La classe Employee ne de\*'finit pas sa propre me\*'thode \fInew()\fR et la me\*'thode \fInew()\fR de la classe Person ne sait rien des Employee (elle ne le doit pas\ : une bonne conception oriente\*'e objet suppose qu'une sous-classe a le droit de connai\*^tre les super-classes dont elle he\*'rite, mais jamais le contraire). Cre\*'ons donc \fIEmployee::new()\fR comme suit\ : .PP .Vb 9 \& sub new { \& my $class = shift; \& my $self = $class\->SUPER::new(); \& $self\->{SALARY} = undef; \& $self\->{ID} = undef; \& $self\->{START_DATE} = undef; \& bless ($self, $class); # reconsecrate \& return $self; \& } .Ve .PP A\*` pre\*'sent, si vous afficher un objet Employee ou Boss, vous verrez ces nouveaux champs. .Sh "He\*'ritage multiple" .IX Subsection "He'ritage multiple" Bon, au risque d'ennuyer les gourous \s-1OO\s0 et de perturber les de\*'butants, il est temps d'avouer que le syste\*`me objet de Perl propose la notion tre\*`s controverse\*'e d'he\*'ritage multiple (ou \s-1HM\s0 pour faire court). Cela signifie qu'au lieu d'he\*'riter d'une classe qui elle\-me\*^me peut he\*'riter d'une autre classe et ainsi de suite, vous pouvez he\*'riter directement de plusieurs classes parentes. Il est vrai que le \s-1HM\s0 peut rendre les choses confuses, me\*^me si en Perl cela l'est un peu moins qu'avec des langages \s-1OO\s0 douteux comme le \*(C+. .PP La manie\*`re dont cela fonctionne est vraiment tre\*`s simple\ : il suffit de mettre plus d'un nom de paquetage dans le tableau \f(CW@ISA\fR. Lorsque Perl cherche une me\*'thode pour votre objet, il regarde dans chacun de ces paquetages dans l'ordre. C'est une recherche re\*'cursive en profondeur d'abord. Supposons un ensemble de tableaux \f(CW@ISA\fR comme\ : .PP .Vb 3 \& @First::ISA = qw( Alpha ); \& @Second::ISA = qw( Beta ); \& @Third::ISA = qw( First Second ); .Ve .PP Si vous avez un objet de la classe Third\ : .PP .Vb 2 \& my $ob = Third\->new(); \& $ob\->spin(); .Ve .PP Comment allons-nous trouver la me\*'thode \fIfind()\fR (ou une me\*'thode \fInew()\fR) ? Puisque la recherche s'effectue en profondeur d'abord, les classes seront explore\*'es dans l'ordre suivant\ : Third, First, Alpha, Second et enfin Beta. .PP En pratique, tre\*`s peu de modules connus font usage de l'\s-1HM\s0. La plupart pre\*'fe\*`rent choisir l'inclusion d'une classe dans une autre pluto\*^t que l'\s-1HM\s0. C'est pourquoi notre objet Person \fIcontient\fR un objet Fullname. Cela ne veut pas dire qu'il en \fIest\fR un. .PP Par contre, il y a un domaine ou\*` l'\s-1HM\s0 de Perl est tre\*`s re\*'pandu\ : lorsqu'une classe emprunte des me\*'thodes a\*` une autre classe. C'est assez commun spe\*'cialement pour quelques classes \*(L"pas tre\*`s objet\*(R" comme Exporter, DynaLoader, AutoLoader et SelfLoader. Ces classes ne proposent pas de constructeurs. Elles n'existent que pour vous permettre d'he\*'riter de leurs me\*'thodes. (Le choix de l'he\*'ritage pluto\*^t que de l'importation traditionnelle n'est pas entie\*`rement clair.) .PP Par exemple, voici le tableau \f(CW@ISA\fR du module \s-1POSIX\s0\ : .PP .Vb 2 \& package POSIX; \& @ISA = qw(Exporter DynaLoader); .Ve .PP Ce module \s-1POSIX\s0 n'est pas vraiment un module objet pas plus qu'un Exporter ou un DynaLoader. Ces derniers ne font que pre\*^ter leur comportement a\*` \s-1POSIX\s0. .PP Pourquoi n'utilise\-t\-on pas plus l'\s-1HM\s0 pour les me\*'thodes objet ? La raison principale re\*'side dans les effets de bord complexes. Par exemple, votre graphe (ce n'est pas obligatoirement un arbre) d'he\*'ritage peut converger vers la me\*^me classe de base. Bien que Perl empe\*^che l'he\*'ritage re\*'cursif, avoir des parents qui se re\*'fe\*`rent a\*` un me\*^me ance\*^tre n'est pas interdit aussi incestueux que cela paraisse. Que se passe-t-il si dans notre classe Third nous voulons que sa me\*'thode \fInew()\fR appelle aussi les constructeurs de ses deux classes parentes ? La notation \s-1SUPER\s0 ne trouvera que la me\*'thode de la premie\*`re classe. D'autre part, qu'arrive\-t\-il si les classes Alpha et Beta ont toutes les deux un ance\*^tre commun ? disons Nought. Si vous parcourez l'arbre d'he\*'ritage afin d'appeler les me\*'thodes cache\*'es, vous finirez par appeler deux fois la me\*'thode \&\fINought::new()\fR, ce qui est su\*^rement une mauvaise chose. .Sh "\s-1UNIVERSAL:\s0 la racine de tous les objets" .IX Subsection "UNIVERSAL: la racine de tous les objets" Ne serait-ce pas pratique si tous les objets he\*'ritaient d'une me\*^me classe de base ? Ainsi, on pourrait avoir des me\*'thodes communes a\*` tous les objets sans avoir a\*` ajouter explicitement cette classe dans chaque tableau \f(CW@ISA\fR. \s-1EN\s0 fait, cela existe de\*'ja\*`. Vous ne le voyez pas, mais Perl suppose tacitement qu'il y a un e\*'le\*'ment supple\*'mentaire a\*` la fin de \f(CW@ISA\fR\ : la classe \s-1UNIVERSAL\s0. Dans la version 5.003, il n'y avait aucune me\*'thode pre\*'de\*'finie dans cette classe, mais vous pouviez y mettre tout ce que vouliez. .PP Maintenant, depuis la version 5.004 (ou quelques versions subversives comme la 5.003_08), \s-1UNIVERSAL\s0 contient de\*'ja\*` des me\*'thodes. Elles sont incluses dans votre binaire Perl et ne consomment donc aucun temps supple\*'mentaire en chargement. Ces me\*'thodes pre\*'de\*'finies comprennent \fIisa()\fR, \fIcan()\fR et \&\s-1\fIVERSION\s0()\fR. \fIisa()\fR vous permet de savoir si un objet (respectivement une classe) \&\*(L"est\*(R" un autre objet (respectivement une autre classe) sans que vous ayez a\*` parcourir vous\-me\*^me la hie\*'rarchie d'he\*'ritage\ : .PP .Vb 2 \& $has_io = $fd\->isa("IO::Handle"); \& $itza_handle = IO::Socket\->isa("IO::Handle"); .Ve .PP La me\*'thode \fIcan()\fR, appele\*'e a\*` partir d'un objet ou d'une classe, vous indique si la chai\*^ne passe\*'e en argument est le nom d'une me\*'thode appelable de cette classe. En fait, elle vous renvoie me\*^me une re\*'fe\*'rence vers la fonction de cette me\*'thode\ : .PP .Vb 1 \& $his_print_method = $obj\->can('as_string'); .Ve .PP Finalement, la me\*'thode \s-1VERSION\s0 ve\*'rifie que la classe (ou la classe de l'objet) posse\*`de une variable globale appele\*'e \f(CW$VERSION\fR dont la valeur est suffisamment grande. Exemple\ : .PP .Vb 2 \& Some_Module\->VERSION(3.0); \& $his_vers = $ob\->VERSION(); .Ve .PP En ge\*'ne\*'ral, vous n'avez pas a\*` appeler \s-1VERSION\s0 vous\-me\*^me. (Souvenez\-vous qu'un nom de fonction tout en majuscule est une convention Perl pour indiquer que cette fonction est parfois appele\*'e automatiquement par Perl.) Dans le cas de \&\s-1VERSION\s0, cela arrive quand vous dites\ : .PP .Vb 1 \& use Some_Module 3.0; .Ve .PP Si vous voulez ajouter un contro\*^le de version a\*` votre classe Person, ajoutez juste dans Person.pm\ : .PP .Vb 1 \& our $VERSION = '1.1'; .Ve .PP Et vous pouvez donc dire dans Employee.pm\ : .PP .Vb 1 \& use Person 1.1; .Ve .PP Et il sera su\*^r que vous avez au moins ce nume\*'ro de version. Ce n'est pas la me\*^me chose que d'exiger un nume\*'ro de version pre\*'cis. Actuellement, aucun me\*'canisme n'existe pour installer simultane\*'ment de multiples versions d'un me\*^me module. Lamentable. .SH "Autre repre\*'sentation d'objet" .IX Header "Autre repre'sentation d'objet" Rien n'oblige a\*` imple\*'menter des objets sous la forme de re\*'fe\*'rence a\*` une table de hachage. Un objet peut\-e\*^tre n'importe quel type de re\*'fe\*'rence tant que cette re\*'fe\*'rence a e\*'te\*' be\*'nie (blessed). Il peut donc e\*^tre une re\*'fe\*'rence a\*` un scalaire, a\*` un tableau ou a\*` du code. .PP Un scalaire peut suffire si l'objet n'a qu'une valeur a\*` stocker. Un tableau fonctionne dans la plupart des cas, mais rend l'he\*'ritage plus de\*'licat puisqu'il vous faut cre\*'er de nouveaux indices pour les classes de\*'rive\*'es. .Sh "Des objets sous forme de tableaux" .IX Subsection "Des objets sous forme de tableaux" Si l'utilisateur de votre classe respecte le contrat et colle a\*` l'interface de\*'clare\*'e, vous pouvez changer l'interface sous-jacente quand vous le voulez. Voici une autre imple\*'mentation qui respecte la me\*^me spe\*'cification d'interface. Cette fois, pour repre\*'senter l'objet, nous utilisons une re\*'fe\*'rence a\*` un tableau pluto\*^t qu'une re\*'fe\*'rence a\*` une table de hachage. .PP .Vb 2 \& package Person; \& use strict; \& \& my($NAME, $AGE, $PEERS) = ( 0 .. 2 ); \& \& ################################################# \& ## le constructeur de Person (version tableau) ## \& ################################################# \& sub new { \& my $self = []; \& $self\->[$NAME] = undef; # this is unnecessary \& $self\->[$AGE] = undef; # as is this \& $self\->[$PEERS] = []; # but this isn't, really \& bless($self); \& return $self; \& } \& \& sub name { \& my $self = shift; \& if (@_) { $self\->[$NAME] = shift } \& return $self\->[$NAME]; \& } \& \& sub age { \& my $self = shift; \& if (@_) { $self\->[$AGE] = shift } \& return $self\->[$AGE]; \& } \& \& sub peers { \& my $self = shift; \& if (@_) { @{ $self\->[$PEERS] } = @_ } \& return @{ $self\->[$PEERS] }; \& } \& \& 1; # so the require or use succeeds .Ve .PP Vous pourriez penser que l'acce\*`s au tableau est plus rapide que l'acce\*`s a\*` la table de hachage, mais ils sont en fait comparables. Le tableau est \fIun tout petit peu\fR plus rapide, mais pas plus de 10 ou 15%, me\*^me si vous remplacez les variables comme \f(CW$AGE\fR par des nombres comme 1. La plus grande diffe\*'rence entre les deux approches est l'utilisation de la me\*'moire. La repre\*'sentation par table de hachage prend plus de me\*'moire que la repre\*'sentation par tableau parce qu'il faut allouer de la me\*'moire pour stocker les cle\*'s d'acce\*`s en plus des valeurs. Par contre, ce n'est pas vraiment mauvais puisque, depuis la version 5.004, le me\*'moire n'est alloue\*'e qu'une seule fois pour une cle\*' donne\*'e inde\*'pendamment du nombre de tables de hachage qui utilisent cette cle\*'. Il est me\*^me pre\*'vu qu'un jour ces diffe\*'rences disparaissent, quand des repre\*'sentations sous-jacentes efficaces seront invente\*'es. .PP Ceci e\*'tant, le petit gain en vitesse (ainsi que celui en me\*'moire) est suffisant pour inciter des programmeurs a\*` choisir la repre\*'sentation par tableau pour des classes simples. Il reste encore un petit proble\*`me d'extensibilite\*'. Par exemple, quand vous aurez besoin de cre\*'er des sous\-classes, vous constaterez que les tables de hachage marchent mieux. .Sh "Des objets sous forme de fermeture (closure)" .IX Subsection "Des objets sous forme de fermeture (closure)" Utiliser une re\*'fe\*'rence a\*` du code pour repre\*'senter un objet ouvre des perspectives fascinantes. Vous pouvez cre\*'er une nouvelle fonction anonyme (une fermeture) qui est la seule a\*` pouvoir acce\*'der aux donne\*'es de l'objet. Parce que vous mettez les donne\*'es dans une table de hachage anonyme dont la porte\*'e lexicale est limite\*'e a\*` la fermeture que vous cre\*'ez, be\*'nissez et renvoyez comme objet. Les me\*'thodes de cette objet appellent la fermeture comme n'importe quelle subroutine normale en lui passant le champ qu'elles veulent modifier. (Oui, le double appel de fonction est lent, mais si vous voulez de la vitesse, vous ne devriez pas utiliser d'objets du tout, non ? :\-) .PP L'utilisation devrait rester comme pre\*'ce\*'demment\ : .PP .Vb 7 \& use Person; \& $him = Person\->new(); \& $him\->name("Jason"); \& $him\->age(23); \& $him\->peers( [ "Norbert", "Rhys", "Phineas" ] ); \& printf "%s is %d years old.\en", $him\->name, $him\->age; \& print "His peers are: ", join(", ", @{$him\->peers}), "\en"; .Ve .PP mais l'imple\*'mentation est radicalement (et peut\-e\*^tre me\*^me sublimement) diffe\*'rente\ : .PP .Vb 1 \& package Person; \& \& sub new { \& my $class = shift; \& my $self = { \& NAME => undef, \& AGE => undef, \& PEERS => [], \& }; \& my $closure = sub { \& my $field = shift; \& if (@_) { $self\->{$field} = shift } \& return $self\->{$field}; \& }; \& bless($closure, $class); \& return $closure; \& } \& \& sub name { &{ $_[0] }("NAME", @_[ 1 .. $#_ ] ) } \& sub age { &{ $_[0] }("AGE", @_[ 1 .. $#_ ] ) } \& sub peers { &{ $_[0] }("PEERS", @_[ 1 .. $#_ ] ) } \& \& 1; .Ve .PP Le concept de fermeture provient de la programmation fonctionnelle et, a\*` ceux qui sont re\*'solument attache\*'s a\*` la programmation proce\*'durale ou a\*` la programmation oriente\*'e objet, l'objet cache\*' derrie\*`re une re\*'fe\*'rence a\*` du code doit vraisemblablement rester myste\*'rieux. L'objet cre\*'e\*' et retourne\*' par la me\*'thode \fInew()\fR n'est plus une re\*'fe\*'rence vers des donne\*'es comme nous l'avons vu auparavant. C'est une re\*'fe\*'rence a\*` du code anonyme qui a acce\*`s a\*` sa propre version des donne\*'es de l'objet (liaison lexicale et instanciation) qui est stocke\*'e dans la variable prive\*'e \f(CW$self\fR. Bien que ce soit la me\*^me fonction (\s-1NDT:\s0 le me\*^me code) a\*` chaque fois, il contient une version diffe\*'rente de \f(CW$self\fR. .PP Quand une me\*'thode comme \f(CW\*(C`$him\->name("Jason")\*(C'\fR est appele\*'e, son \*(L"ze\*'roie\*`me\*(R" argument implicite est l'objet appelant \*(-- exactement comme pour tous les appels de me\*'thodes. Mais dans notre cas, c'est une re\*'fe\*'rence a\*` notre code (quelque chose comme un pointeur sur fonction en \*(C+, mais avec une liaison vers des variables lexicales). A\*` part l'appeler, on ne peut pas faire grand chose d'une re\*'fe\*'rence vers du code. C'est donc exactement ce que nous faisons en disant \f(CW\*(C`&{$_[0]}\*(C'\fR. C'est juste un appel a\*` une fonction et non pas un appel de me\*'thode. Le premier argument est la chai\*^ne \*(L"\s-1NAME\s0\*(R", tous les autres arguments sont ceux qui ont e\*'te\*' passe\*'s a\*` la me\*'thode. .PP Lors de l'exe\*'cution de la fermeture cre\*'e\*'e par \fInew()\fR, la re\*'fe\*'rence \&\f(CW$self\fR a\*` la table de hachage devient soudainement visible. La fermeture retrouve son premier argument (\*(L"\s-1NAME\s0\*(R" dans ce cas puisque c'est ce que lui passe la me\*'thode \fIname()\fR) et l'utilise comme cle\*' d'acce\*`s a\*` cette table de hachage prive\*'e qui est cache\*'e dans sa propre version de \f(CW$self\fR. .PP Rien ne permet a\*` quiconque d'acce\*'der a\*` ces donne\*'es cache\*'es en dehors de l'exe\*'cution de cette me\*'thode. Ou presque rien\ : vous \fIpourriez\fR utiliser le deboggueur en allant pas a\*` pas jusque dans le code de la me\*'thode et voir les donne\*'es, mais en dehors de cela aucune chance d'y acce\*'der. .PP Bon, si tout c\*,a n'inte\*'resse pas les adeptes de Scheme, je ne vois ce qui pourrait les inte\*'resser. La transposition de ces techniques en \*(C+, Java ou tout autre langage de conception statique est laisse\*'e comme exercice futile a\*` leurs aficionados. .PP Via la fonction \fIcaller()\fR, vous pouvez me\*^me ajouter un peu plus de confidentialite\*' en contraignant la fermeture a\*` n'accepter que les appels provenant de son propre paquetage. Cela devrait sans aucun doute satisfaire les plus exigeants... .PP Vous avez eu ici de quoi satisfaire votre orgueil (la troisie\*`me vertu principale du programmeur). Plus se\*'rieusement, l'orgueil est tout simplement la fierte\*' de l'artisan qui vient d'e\*'crire un bout de code bien conc\*,u. .SH "AUTOLOAD: les me\*'thodes mandataires" .IX Header "AUTOLOAD: les me'thodes mandataires" L'auto\-chargement (ou autoload) est un moyen d'intercepter l'appel a\*` une me\*'thode non de\*'finie. Une subroutine d'auto\-chargement peux soit cre\*'er une nouvelle fonction au vol, soit en charger une depuis le disque, soit l'e\*'valuer elle\-me\*^me. Cette strate\*'gie de de\*'finition au vol est ce qu'on appelle l'auto\-chargement. .PP Mais ce n'est qu'une approche possible. Dans une autre approche, c'est la me\*'thode d'auto\-chargement qui fournit elle\-me\*^me le service demande\*'e. Utilise\*'e de cette manie\*`re, on peut alors conside\*'rer cette me\*'thode d'auto\-chargement comme une me\*'thode \*(L"proxy\*(R". .PP Quand Perl essaie d'appeler une fonction non de\*'finie dans un paquetage particulier et que cette fonction n'est pas de\*'finie, il cherche alors dans le me\*^me paquetage une fonction appele\*' \s-1AUTOLOAD\s0. Si elle existe, elle est appele\*'e avec les me\*^mes arguments que la fonction originale. Le nom complet de la fonction dans la variable \f(CW$AUTOLOAD\fR du paquetage. Une fois appele\*'e, la fonction peut faire tout ce qu'elle veut et, entre autres, de\*'finir une nouvelle fonction avec le bon nom, puis faire une sorte de \f(CW\*(C`goto\*(C'\fR vers elle en s'effac\*,ant de la pile d'appel. .PP Quel rapport avec les objets ? Apre\*`s tout, nous ne parlons que de fonctions, pas de me\*'thodes. En fait, puisqu'une me\*'thode n'est qu'une fonction avec un argument supple\*'mentaire et quelques se\*'mantiques sympathiques permettant de la retrouver, nous pouvons aussi utiliser l'auto\-chargement pour les me\*'thodes. Perl ne commence la recherche de me\*'thodes par auto-chargement que lorsqu'il a fini l'exploration via \f(CW@ISA\fR. Certains programmeurs ont me\*^me de\*'fini une me\*'thode \s-1UNIVERSAL::AUTOLOAD\s0 pour intercepter tous les appels a\*` des me\*'thodes non de\*'finies pour n'importe quelle sorte d'objets. .Sh "Me\*'thodes auto\-charge\*'es d'acce\*`s aux donne\*'es" .IX Subsection "Me'thodes auto-charge'es d'acce`s aux donne'es" Vous e\*^tes peut\-e\*^tre reste\*' un peu sceptique devant la duplication de code que nous avons expose\*' tout d'abord dans la classe Person, puis dans la classe Employee. Chaque me\*'thode d'acce\*`s aux donne\*'es de la table de hachage est virtuellement identique. Cela devrait chatouiller votre plus grande vertu de programmeur: l'impatience. Mais votre paresse l'a emporte\*' et vous n'avez rien fait. Heureusement, les me\*'thodes proxy sont le reme\*`de a\*` ce proble\*`me. .PP Au lieu d'e\*'crire une nouvelle fonction a\*` chaque fois que nous avons besoin d'un nouveau champ, nous allons utiliser le me\*'canisme d'auto\-chargement pour ge\*'ne\*'rer (en fait, simuler) les me\*'thodes au vol. Pour ve\*'rifier que nous acce\*'dons a\*` un membre valide, nous le rechercherons dans le champ \f(CW\*(C`_permitted\*(C'\fR (qui, en anglais, se prononce \*(L"under\-permitted\*(R"). Ce champ est une re\*'fe\*'rence a\*` une table de hachage de porte\*'e lexicale limite\*'e au fichier (comme une variable C statique d'un fichier) contenant les champs autorise\*'s et appele\*'e \&\f(CW%fields\fR. Pourquoi le caracte\*`re de soulignement ? Pour le me\*^me raison que celui de _CENSUS\ : c'est un indicateur qui signifie \*(L"a\*` usage interne seulement\*(R". .PP Voici a\*` quoi ressemblent le code d'initialisation et le constructeur de la classe si nous suivons cette approche\ : .PP .Vb 3 \& package Person; \& use Carp; \& our $AUTOLOAD; # it's a package global \& \& my %fields = ( \& name => undef, \& age => undef, \& peers => undef, \& ); \& \& sub new { \& my $class = shift; \& my $self = { \& _permitted => \e%fields, \& %fields, \& }; \& bless $self, $class; \& return $self; \& } .Ve .PP Si nous voulons spe\*'cifier des valeurs par de\*'faut pour nos champs, nous pouvons remplacer les \f(CW\*(C`undef\*(C'\fR dans la table de hachage \f(CW%fields\fR. .PP Avez-vous remarque\*' comment nous stockons la re\*'fe\*'rence a\*` nos donne\*'es de classe dans l'objet lui\-me\*^me ? Souvenez-vous qu'il est fondamental d'acce\*'der aux donne\*'es de classe a\*` travers l'objet lui\-me\*^me pluto\*^t que d'utiliser directement \&\f(CW%fields\fR dans les me\*'thodes. Sinon vous ne pourrez pas convenablement he\*'riter. .PP La vraie magie re\*'side dans notre me\*'thode proxy qui ge\*'rera tous les appels a\*` des me\*'thodes non de\*'finies pour des objets de la classe Person (ou des sous-classes de Person). Elle doit s'appeler \s-1AUTOLOAD\s0. Encore une fois, son nom est tout en majuscule parce qu'elle est implicitement appele\*'e par Perl et non pas directement par l'utilisateur. .PP .Vb 4 \& sub AUTOLOAD { \& my $self = shift; \& my $type = ref($self) \& or croak "$self is not an object"; \& \& my $name = $AUTOLOAD; \& $name =~ s/.*://; # strip fully\-qualified portion \& \& unless (exists $self\->{_permitted}\->{$name} ) { \& croak "Can't access `$name' field in class $type"; \& } \& \& if (@_) { \& return $self\->{$name} = shift; \& } else { \& return $self\->{$name}; \& } \& } .Ve .PP Assez chouette, non ? La seule chose a\*` faire pour ajouter de nouveaux champs est de modifier \f(CW%fields\fR. Il n'y a aucune nouvelle fonction a\*` e\*'crire. .PP J'aurais me\*^me pu supprimer comple\*`tement le champ \f(CW\*(C`_permitted\*(C'\fR, mais je voulais illustrer comment stocker une re\*'fe\*'rence a\*` une donne\*'e de classe dans un objet de manie\*`re a\*` ne pas acce\*'der a\*` cette donne\*'e directement dans les me\*'thodes. .Sh "Me\*'thodes d'acce\*`s aux donne\*'es auto\-charge\*'es et he\*'rite\*'es" .IX Subsection "Me'thodes d'acce`s aux donne'es auto-charge'es et he'rite'es" Qu'en est-il de l'he\*'ritage ? Pouvons-nous de\*'finir la classe Employee de la me\*^me manie\*`re ? Oui, si nous faisons un peu attention. .PP Voici comment faire\ : .PP .Vb 4 \& package Employee; \& use Person; \& use strict; \& our @ISA = qw(Person); \& \& my %fields = ( \& id => undef, \& salary => undef, \& ); \& \& sub new { \& my $class = shift; \& my $self = $class\->SUPER::new(); \& my($element); \& foreach $element (keys %fields) { \& $self\->{_permitted}\->{$element} = $fields{$element}; \& } \& @{$self}{keys %fields} = values %fields; \& return $self; \& } .Ve .PP Une fois cela fait, nous n'avons me\*^me pas a\*` de\*'finir une fonction \s-1AUTOLOAD\s0 dans le paquetage Employee puisque la version fournie par Person via l'he\*'ritage fonctionne tre\*`s bien. .SH "Me\*'ta\-outils classiques" .IX Header "Me'ta-outils classiques" Me\*^me si l'approche par les me\*'thodes proxy est plus pratique pour fabriquer des classes ressemblant a\*` des structures que l'approche fastidieuse ne\*'cessitant le codage de chacune des fonctions d'acce\*`s, elle laisse encore un peu a\*` de\*'sirer. Par exemple, vous devez ge\*'rer les appels errone\*'s que vous ne voulez pas capter via votre proxy. Il faut aussi faire attention lors de l'he\*'ritage comme nous l'avons montre\*' pre\*'ce\*'demment. .PP Les programmeurs Perl ont re\*'pondu aux besoins en cre\*'ant diffe\*'rentes classes de construction de classes. Ces me\*'ta\-classes sont des classes qui cre\*'ent d'autres classes. Deux d'entre elles me\*'ritent notre attention\ : Class::Struct et Alias. Celles-ci ainsi que d'autres classes apparente\*'es peuvent e\*^tre re\*'cupe\*'re\*'es dans le re\*'pertoire modules du \s-1CPAN\s0. .Sh "Class::Struct" .IX Subsection "Class::Struct" La plus vieille de toutes est Class::Struct. En fait, sa syntaxe et son interface e\*'taient esquisse\*'es alors me\*^me que perl5 n'existait pas encore. Elle fournit le moyen de \*(L"de\*'clarer\*(R" une classe d'objets dont le type de chaque champ est spe\*'cifie\*'. La fonction permettant de faire cela s'appelle (rien de surprenant) \fIstruct()\fR. Puisque les structures ou les enregistrements ne sont pas des types de base de Perl, a\*` chaque fois que vous voulez cre\*'er une classe pour fournir un objet de type structure, vous devez vous\-me\*^me de\*'finir la me\*'thode \fInew()\fR ainsi que les me\*'thodes d'acce\*`s aux donne\*'es pour chacun des champs. Tre\*`s rapidement, vous trouverez cela enquiquinant (pour ne pas dire autre chose). La fonction \fIClass::Struct::struct()\fR vous soulagera de cette ta\*^che ennuyeuse. .PP Voici un simple exemple d'utilisation\ : .PP .Vb 2 \& use Class::Struct qw(struct); \& use Jobbie; # de\*'fini par l'utilisateur; voir plus bas \& \& struct 'Fred' => { \& one => '$', \& many => '@', \& profession => 'Jobbie', # n'appelle pas de Jobbie\->new() \& }; \& \& $ob = Fred\->new(profession => Jobbie\->new()); \& $ob\->one("hmmmm"); \& \& $ob\->many(0, "here"); \& $ob\->many(1, "you"); \& $ob\->many(2, "go"); \& print "Just set: ", $ob\->many(2), "\en"; \& \& $ob\->profession\->salary(10_000); .Ve .PP Les types des champs dans la structure peuvent e\*^tre de\*'clare\*'s comme des types de base de Perl ou comme des types utilisateurs (classes). Les types utilisateurs seront initialise\*'s par l'appel de la me\*'thode \fInew()\fR de la classe. .PP Attention au fait que l'objet \f(CW\*(C`Jobbie\*(C'\fR n'est pas cre\*'e\*' automatiquement par la me\*'thode \fInew()\fR de la class \f(CW\*(C`Fred\*(C'\fR. Vous devez donc spe\*'cifier un objet \f(CW\*(C`Jobbie\*(C'\fR lorsque vous cre\*'ez un instance de \f(CW\*(C`Fred\*(C'\fR. .PP Voici un exemple re\*'el d'utilisation de la ge\*'ne\*'ration de structure. Supposons que vous vouliez modifier le fonctionnement des fonctions Perl \fIgethostbyname()\fR et \fIgethostbyaddr()\fR pour qu'elles renvoient des objets qui fonctionnent comme des structures C. Nous ne voulons pas d'\s-1OO\s0, ici. Nous voulons seulement que ces objets se comportent comme des structures au sens C du terme. .PP .Vb 5 \& use Socket; \& use Net::hostent; \& $h = gethostbyname("perl.com"); # object return \& printf "perl.com's real name is %s, address %s\en", \& $h\->name, inet_ntoa($h\->addr); .Ve .PP Voici comment faire en utilisant le module Class::Struct. Le point crucial est cet appel\ : .PP .Vb 7 \& struct 'Net::hostent' => [ # notez le crochet \& name => '$', \& aliases => '@', \& addrtype => '$', \& 'length' => '$', \& addr_list => '@', \& ]; .Ve .PP qui cre\*'e les me\*'thodes d'objets avec les bons noms et types. Il cre\*'e me\*^me la me\*'thode \fInew()\fR pour vous. .PP Vous auriez pu imple\*'menter votre objet de la manie\*`re suivante\ : .PP .Vb 7 \& struct 'Net::hostent' => { # notez l'accolade \& name => '$', \& aliases => '@', \& addrtype => '$', \& 'length' => '$', \& addr_list => '@', \& }; .Ve .PP Class::Struct aurait alors utilise\*' une table de hachage anonyme comme type d'objet pluto\*^t qu'un tableau anonyme. Le tableau est plus rapide et plus petit, mais la table de hachage fonctionne mieux si e\*'ventuellement vous voulez faire de l'he\*'ritage. Puisque dans le cas de cet objet de type structure nous ne pre\*'voyons pas d'he\*'ritage, nous opterons cette fois-ci pluto\*^t pour la rapidite\*' et le gain de place que pour une meilleure flexibilite\*'. .PP Voici l'imple\*'mentation comple\*`te\ : .PP .Vb 2 \& package Net::hostent; \& use strict; \& \& BEGIN { \& use Exporter (); \& our @EXPORT = qw(gethostbyname gethostbyaddr gethost); \& our @EXPORT_OK = qw( \& $h_name @h_aliases \& $h_addrtype $h_length \& @h_addr_list $h_addr \& ); \& our %EXPORT_TAGS = ( FIELDS => [ @EXPORT_OK, @EXPORT ] ); \& } \& our @EXPORT_OK; \& \& # Class::Struct interdit l'usage de @ISA \& sub import { goto &Exporter::import } \& \& use Class::Struct qw(struct); \& struct 'Net::hostent' => [ \& name => '$', \& aliases => '@', \& addrtype => '$', \& 'length' => '$', \& addr_list => '@', \& ]; \& \& sub addr { shift\->addr_list\->[0] } \& \& sub populate (@) { \& return unless @_; \& my $hob = new(); # Class::Struct made this! \& $h_name = $hob\->[0] = $_[0]; \& @h_aliases = @{ $hob\->[1] } = split ' ', $_[1]; \& $h_addrtype = $hob\->[2] = $_[2]; \& $h_length = $hob\->[3] = $_[3]; \& $h_addr = $_[4]; \& @h_addr_list = @{ $hob\->[4] } = @_[ (4 .. $#_) ]; \& return $hob; \& } \& \& sub gethostbyname ($) { populate(CORE::gethostbyname(shift)) } \& \& sub gethostbyaddr ($;$) { \& my ($addr, $addrtype); \& $addr = shift; \& require Socket unless @_; \& $addrtype = @_ ? shift : Socket::AF_INET(); \& populate(CORE::gethostbyaddr($addr, $addrtype)) \& } \& \& sub gethost($) { \& if ($_[0] =~ /^\ed+(?:\e.\ed+(?:\e.\ed+(?:\e.\ed+)?)?)?$/) { \& require Socket; \& &gethostbyaddr(Socket::inet_aton(shift)); \& } else { \& &gethostbyname; \& } \& } \& \& 1; .Ve .PP Outre la cre\*'ation dynamique de classes, nous n'avons qu'effleure\*' certains concepts comme la rede\*'finition des fonctions de base, l'import/export de bits, le prototypage de fonctions, les raccourcis d'appels de fonctions via \&\f(CW&whatever\fR et le remplacement de fonction par \f(CW\*(C`goto &whatever\*(C'\fR. Ils ont tous un sens du point de vue des modules traditionnels, mais comme vous avez pu le constater, vous pouvez aussi les utiliser dans un module objet. .PP Dans la version 5.004 de Perl, vous pouvez trouver d'autres modules objet qui rede\*'finissent les fonctions de base avec des structures\ : File::stat, Net::hostent, Net::netent, Net::protoent, Net::servent, Time::gmtime, Time::localtime, User::grent et User::pwent. Le composant final du nom de tous ces modules est entie\*`rement en minuscules qui, par convention, sont re\*'serve\*'s aux pragma du compilateur parce qu'ils modifient la compilation et les fonctions internes. Ils proposent en outre les noms de type qu'un programmeur C attend. .Sh "Les donne\*'es membres comme des variables" .IX Subsection "Les donne'es membres comme des variables" Si vous utilisez des objets \*(C+, vous e\*^tes habitue\*' a\*` acce\*'der aux donne\*'es membres d'un objet par simples variables lorsque vous e\*^tes dans une me\*'thode. Le module Alias offre entre autres cette fonctionnalite\*', ainsi que la possibilite\*' d'avoir des me\*'thodes prive\*'es que les objets peuvent appeler, mais qui ne peuvent l'e\*^tre depuis l'exte\*'rieur de la classe. .PP Voici un exemple de classe Person cre\*'e\*'e en utilisant le module Alias. Quand vous mettez a\*` jour ces instances des variables, vous mettez a\*` jour automagiquement les champs correspondants de la table de hachage. Pratique, non ? .PP .Vb 1 \& package Person; \& \& # C'est la me\*^me chose qu'avant... \& sub new { \& my $class = shift; \& my $self = { \& NAME => undef, \& AGE => undef, \& PEERS => [], \& }; \& bless($self, $class); \& return $self; \& } \& \& use Alias qw(attr); \& our ($NAME $AGE $PEERS); \& \& sub name { \& my $self = attr shift; \& if (@_) { $NAME = shift; } \& return $NAME; \& } \& \& sub age { \& my $self = attr shift; \& if (@_) { $AGE = shift; } \& return $AGE; \& } \& \& sub peers { \& my $self = attr shift; \& if (@_) { @PEERS = @_; } \& return @PEERS; \& } \& \& sub exclaim { \& my $self = attr shift; \& return sprintf "Hi, I'm %s, age %d, working with %s", \& $NAME, $AGE, join(", ", @PEERS); \& } \& \& sub happy_birthday { \& my $self = attr shift; \& return ++$AGE; \& } .Ve .PP La de\*'claration \f(CW\*(C`our\*(C'\fR est ne\*'cessaire parce que le module Alias bidouille les variables globales du paquetage qui ont le me\*^me nom que les champs. Pour pouvoir utiliser des variables globales avec \f(CW\*(C`use strict\*(C'\fR, vous devez les de\*'clarer au pre\*'alable. Ces variables globales du paquetage sont localise\*'es dans le bloc englobant l'appel a\*` \fIattr()\fR exactement comme si vous aviez utilise\*' \&\fIlocal()\fR. Par contre, cela signifie qu'elles sont conside\*'re\*'es comme des variables globales avec des valeurs temporaires comme avec tout autre \fIlocal()\fR. .PP Il serait joli de combiner Alias avec quelque chose comme Class::Struct ou Class::MethodMaker. .SH "NOTES" .IX Header "NOTES" .Sh "Terminologie objet" .IX Subsection "Terminologie objet" Dans la litte\*'rature \s-1OO\s0, un grand nombre de mots diffe\*'rents sont utilise\*'s pour nommer quelques concepts. Si vous n'e\*^tes pas de\*'ja\*` un programmeur objet vous n'avez pas a\*` vous en pre\*'occuper. Dans le cas contraire, vous aimeriez su\*^rement savoir a\*` quoi correspondent ces concepts en Perl. .PP Par exemple, un objet est souvent appele\*' une \fIinstance\fR d'une classe et les me\*'thodes objet \fIme\*'thodes d'instance\fR. Les champs spe\*'cifiques de chaque objet sont souvent appele\*'s \fIdonne\*'es d'instance\fR ou \fIattributs d'objet\fR. Tandis que les champs communs a\*` tous les membres de la classe sont nomme\*'s \fIdonne\*'e de classe\fR, \fIattributs de classe\fR ou . .PP \&\fIClasse de base\fR, \fIclasse ge\*'ne\*'rique\fR et \fIsuper classe\fR recouvrent tous la me\*^me notion. \fIClasse de\*'rive\*'e\fR, \fIclasse spe\*'cifique\fR et \fIsous-classe\fR de\*'crivent aussi la me\*^me chose. .PP Les programmeurs \*(C+ ont des \fIme\*'thodes statiques\fR et des \fIme\*'thodes virtuelles\fR, Perl ne propose que les \fIme\*'thodes de classe\fR et les \&\fIme\*'thodes d'objet\fR. En fait, Perl n'a que des me\*'thodes. Qu'une me\*'thode s'applique a\*` une classe ou a\*` un objet ne se fait qu'a\*` l'usage. Vous pouvez accidentellement appeler une me\*'thode de classe (une me\*'thode qui attend une chai\*^ne comme argument implicite) comme une me\*'thode d'objet (une me\*'thode qui attend une re\*'fe\*'rence comme argument implicite) ou vice\-versa. .PP Du point de vue \*(C+, toutes les me\*'thodes en Perl sont virtuelles. C'est la raison pour laquelle il n'y a jamais de ve\*'rification des arguments par rapport aux prototypes des fonctions comme cela peut se faire pour les fonctions pre\*'de\*'finies ou de\*'finies par l'utilisateur. .PP Puisque une classe... (\s-1NDT:\s0 je n'ai pas compris ce paragraphe !) Because a class is itself something of an object, Perl's classes can be taken as describing both a \*(L"class as meta\-object\*(R" (also called \fIobject factory\fR) philosophy and the \*(L"class as type definition\*(R" (\fIdeclaring\fR behaviour, not \&\fIdefining\fR mechanism) idea. \*(C+ supports the latter notion, but not the former. .SH "VOIR AUSSI" .IX Header "VOIR AUSSI" Vous trouverez sans aucun doute de plus amples informations en lisant les documentations suivantes\ : perlmod, perlref, perlobj, perlbot, perltie et overload. .PP perlboot est un tutoriel simple et facile pour la programmation oriente\*'e objet. .PP perltooc donne plus de de\*'tails sur les donne\*'es de classe. .PP Quelques modules particulie\*`rement inte\*'ressants sont Class::Accessor, Class::Class, Class::Contract, Class::Data::Inheritable, Class::MethodMaker et Tie::SecureHash. .SH "AUTEURS ET COPYRIGHT" .IX Header "AUTEURS ET COPYRIGHT" Copyright (c) 1997, 1998 Tom Christiansen Tous droits re\*'serve\*'s. .PP Cette documentation est libre ; vous pouvez la redistribuer et/ou la modifier sous les me\*^mes conditions que Perl lui\-me\*^me. .PP Inde\*'pendamment de sa distribution, tous les exemples de code de ce fichier sont ici place\*'s dans le domaine public. Vous e\*^tes autorise\*'s et encourage\*'s a\*` utiliser ce code dans vos programmes que ce soit pour votre plaisir ou pour un profit. Un simple commentaire dans le code en pre\*'cisant l'origine serait de bonne courtoisie mais n'est pas obligatoire. .Sh "Remerciements" .IX Subsection "Remerciements" Merci a\*` Larry Wall, Roderick Schertler, Gurusamy Sarathy, Dean Roehrich, Raphael Manfredi, Brent Halsey, Greg Bacon, Brad Appleton et a\*` beaucoup d'autres pour leurs remarques pertinentes. .SH "TRADUCTION" .IX Header "TRADUCTION" La traduction franc\*,aise est distribue\*'e avec les me\*^me droits que sa version originale (voir ci\-dessus). .Sh "Version" .IX Subsection "Version" Cette traduction franc\*,aise correspond a\*` la version anglaise distribue\*'e avec perl 5.8.8. Pour en savoir plus concernant ces traductions, consultez . .Sh "Traducteur" .IX Subsection "Traducteur" Tradiction et mise a\*` jour\ : Paul Gaborit (Paul.Gaborit at enstimac.fr). .Sh "Relecture" .IX Subsection "Relecture" Philippe de Visme (\fIphilippe@devisme.com\fR).