.\" 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 "PERLTOOC 1" .TH PERLTOOC 1 "2006-03-08" "DocFr" "User Contributed Perl Documentation" .SH "NAME/NOM" .IX Header "NAME/NOM" perltooc \- Le tutoriel de Tom pour les donne\*'es de classe \s-1OO\s0 en Perl .SH "DESCRIPTION" .IX Header "DESCRIPTION" Lors de la conception d'une classe objet, vous ressentez parfois le besoin de partager des choses communes a\*` tous les objets de cette classe. De tels \fIattributs de classe\fR ressemblent beaucoup a\*` des variables globales a\*` la classe tout entie\*`re, mais a\*` la diffe\*'rence des variables globales a\*` un programme, les attributs de classe n'ont de signification que pour la classe elle\-me\*^me. .PP Voici quelques exemples pratiques d'utilisation d'attributs de classe : .IP "\(bu" 4 me\*'moriser le nombre d'objets cre\*'e\*'s, ou savoir combien existent encore ; .IP "\(bu" 4 extraire le nom ou le descripteur de fichier d'un fichier de log utilise\*' par une me\*'thode de debugging ; .IP "\(bu" 4 acce\*'der a\*` des donne\*'es communes, par exemple le total des ope\*'rations ge\*'re\*'es par l'ensemble des \s-1ATM\s0 un jour donne\*' ; .IP "\(bu" 4 acce\*'der au dernier objet cre\*'e\*' pour une classe, ou bien a\*` l'objet le plus utilise\*', ou encore extraire une liste d'objets. .PP A la diffe\*'rence d'une variable globale, les attributs de classe ne sont pas directement accessibles. En revanche, on peut connai\*^tre \- et e\*'ventuellement modifier \- leur e\*'tat ou leur valeur, et ce uniquement au moyen des \fIme\*'thodes de classe\fR. Ces me\*'thodes d'acce\*`s aux attributs de classe ressemblent, dans l'esprit et dans leur re\*'alisation, aux me\*'thodes d'acce\*`s utilise\*'es pour manipuler l'e\*'tat des attributs d'instance d'un objet de la classe. Elles fournissent un coupe-feu efficace entre l'interface et l'imple\*'mentation. .PP Il est possible d'autoriser l'acce\*`s aux attributs de classe soit par le nom de classe, soit par une instance quelconque de cette classe. Si \f(CW$an_object\fR est un objet du type \f(CW\*(C`Some_Class\*(C'\fR, et si la me\*'thode \&\f(CW&Somme_Class::population_count\fR acce\*`de aux attributs de classe, alors les deux appels suivants sont possibles, et quasiment e\*'quivalents. .PP .Vb 2 \& Some_Class\->population_count() \& $an_object\->population_count() .Ve .PP Maintenant, ou\*` va-t-on ranger la variable a\*` laquelle acce\*`de cette me\*'thode ? A la diffe\*'rence de langages plus restrictifs tels que \*(C+, dans lesquels ces variables portent le nom de donne\*'es membres statiques, Perl ne fournit pas de me\*'canisme syntaxique pour de\*'clarer les attributs de classe, pas plus qu'il ne fournit de me\*'canisme syntaxique pour de\*'clarer les attributs d'instance. Perl fournit au de\*'veloppeur un large ensemble de caracte\*'ristiques puissantes mais flexibles dont il peut se servir pour tel ou tel besoin particulier en fonction de la situation. .PP Une classe Perl est typiquement imple\*'mente\*'e dans un module. Un module contient deux ensembles de caracte\*'ristiques comple\*'mentaires : un paquetage pour l'interface avec le reste du monde, et un espace lexical pour l'intimite\*'. On peut utiliser l'un ou l'autre de ces me\*'canismes pour imple\*'menter des attributs de classe. Ce qui signifie que vous devez choisir de loger vos attributs de classe soit dans les variables de paquetage, soit dans les variables lexicales. .PP Mais ce ne sont pas les seules de\*'cisions a\*` prendre. Si l'on choisit d'utiliser les variables de paquetage, il faudra e\*'galement de\*'cider si les me\*'thodes d'acce\*`s aux attributs seront sensibles ou insensibles a\*` l'he\*'ritage. Si a\*` l'inverse le choix se porte sur les variables lexicales, il faudra de\*'cider si l'on e\*'tend leur visibilite\*' au fichier entier, ou bien si on limite exclusivement leur acce\*`s aux me\*'thodes imple\*'mentant ces attributs. .SH "Donne\*'es de classe pre\*^tes a\*` l'emploi" .IX Header "Donne'es de classe pre^tes a` l'emploi" Lorsqu'on se trouve devant un proble\*`me difficile, le plus simple est de laisser un autre le re\*'soudre a\*` votre place ! Si c'est le cas, Class::Data::Inheritable (disponible sur un serveur \s-1CPAN\s0 pre\*`s de chez vous) offre une solution \*(L"cle\*' en main\*(R" au proble\*`me des donne\*'es de classe, solution qui utilise les fermetures. Aussi, avant de vous e\*'garer dans ce document, je vous conseille de jeter un coup d'oeil a\*` ce module. .SH "Donne\*'es de classe en tant que variables de paquetage" .IX Header "Donne'es de classe en tant que variables de paquetage" Le choix le plus naturel est d'utiliser les variables de paquetage pour stocker les attributs de classe, tout simplement parce qu'une classe Perl est un paquetage. Ceci simplifie, pour chaque classe, l'imple\*'mentation de ses propres attributs de classe. Supposons que l'on ait une classe nomme\*'e Some_Class, ne\*'cessitant une paire d'attributs diffe\*'rents que vous voulez rendre accessibles a\*` la classe entie\*`re. La chose la plus simple a\*` faire est d'utiliser pour ces attributs des variables de paquetage telles que \&\f(CW$Some_Class::CData1\fR et \f(CW$Some_Class::CData2\fR. Mais nous n'encouragerons pas l'utilisateur a\*` utiliser directement ces donne\*'es, c'est pourquoi nous lui fournirons des me\*'thodes d'acce\*`s a\*` ces variables. .PP Dans les me\*'thodes d'acce\*`s ci\-dessous, nous allons pour le moment ignorer le premier argument \- la partie situe\*'e a\*` gauche de la fle\*`che de l'appel de la me\*'thode, qui est soit un nom de classe soit une re\*'fe\*'rence d'objet. .PP .Vb 11 \& package Some_Class; \& sub CData1 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $Some_Class::CData1 = shift if @_; \& return $Some_Class::CData1; \& } \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $Some_Class::CData2 = shift if @_; \& return $Some_Class::CData2; \& } .Ve .PP Cette technique est tre\*`s claire, et devrait e\*^tre tre\*`s simple a\*` mettre en oeuvre me\*^me pour un novice de la programmation Perl. En qualifiant comple\*`tement les variables de paquetage, celles-ci apparaissent clairement a\*` la lecture du code. Malheureusement, si l'on orthographie mal l'une d'entre elles, on introduit une erreur difficile a\*` localiser. Par ailleurs, il est parfois de\*'concertant de trouver le nom de la classe code\*' \*(L"en dur\*(R" aussi souvent. .PP Ces deux proble\*`mes trouvent facilement leur solution. Il suffit simplement d'ajouter le pragma \f(CW\*(C`use strict\*(C'\fR, puis de pre\*'\-de\*'clarer les variables de paquetage. (l'ope\*'rateur \f(CW\*(C`our\*(C'\fR a vu le jour dans la version 5.6 de Perl, et repre\*'sente pour les variables globales du paquetage ce que \f(CW\*(C`my\*(C'\fR repre\*'sente pour les variables lexicales.) .PP .Vb 10 \& package Some_Class; \& use strict; \& our($CData1, $CData2); # our() est nouveau depuis perl5.6 \& sub CData1 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $CData1 = shift if @_; \& return $CData1; \& } \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $CData2 = shift if @_; \& return $CData2; \& } .Ve .PP Tout comme pour les autres variables globales, certains programmeurs pre\*'fe\*`rent mettre en majuscule la premie\*`re lettre de leurs variables de paquetage. C'est relativement clair, mais si l'on ne qualifie pas comple\*`tement les variables de paquetage, on risque de perdre leur signification a\*` la lecture du code. On peut facilement pallier a\*` cela en choisissant de meilleurs noms. .Sh "Mettre tous ses oeufs dans le me\*^me panier" .IX Subsection "Mettre tous ses oeufs dans le me^me panier" L'e\*'nume\*'ration des me\*'thodes d'acce\*`s aux donne\*'es de classe devient rapidement ennuyeuse, de manie\*`re similaire a\*` l'e\*'nume\*'ration des me\*'thodes d'acce\*`s aux attributs d'instance (voir perltoot). Cette re\*'pe\*'tition contredit la premie\*`re vertu du programmeur, la paresse, qui se manifeste ici par le de\*'sir inne\*' du programmeur de factoriser le code duplique\*' partout ou\*` cela est possible. .PP Voici ce que nous allons faire. Tout d'abord, cre\*'er un simple hash qui contiendra tous les attributs de classe. .PP .Vb 6 \& package Some_Class; \& use strict; \& our %ClassData = ( # our() est nouveau depuis perl5.6 \& CData1 => "", \& CData2 => "", \& ); .Ve .PP En utilisant les fermetures (voir perlref) ainsi que l'acce\*`s direct a\*` la table de symboles du paquetage (voir perlmod), nous allons cloner une me\*'thode d'acce\*`s pour chaque cle\*' du hash \f(CW%ClassData\fR. Chacune de ces me\*'thodes e\*'valuera ou modifiera la valeur d'un attribut de classe spe\*'cifique nomme\*'. .PP .Vb 8 \& for my $datum (keys %ClassData) { \& no strict "refs"; # pour enregistrer les nouvelles me\*'thodes dans le paquetage \& *$datum = sub { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $ClassData{$datum} = shift if @_; \& return $ClassData{$datum}; \& } \& } .Ve .PP En re\*'alite\*' nous pourrions utiliser une solution utilisant une me\*'thode &AUTOLOAD, mais cette approche est loin d'e\*^tre satisfaisante. La fonction devrait distinguer les attributs d'objets des attributs de classe ; elle pourrait interfe\*'rer avec l'he\*'ritage ; et elle devrait tenir compte de \s-1DESTROY\s0. Une telle complexite\*' n'est pas ne\*'cessaire dans la plupart des cas, et certainement pas dans celui\-ci. .PP Pourquoi l'utilisation de \f(CW\*(C`no strict refs\*(C'\fR dans la boucle ? Nous sommes en train de manipuler la table des symboles pour introduire de nouveaux noms de fonctions en utilisant des re\*'fe\*'rences symboliques (nommage indirect), ce qu'aurait interdit le pragma \f(CW\*(C`strict\*(C'\fR. Normalement, les re\*'fe\*'rences symboliques repre\*'sentent au mieux une manie\*`re d'esquiver la question. Ce n'est pas uniquement du\*^ au fait qu'elles puissent e\*^tre utilise\*'es accidentellement alors que vous n'y pensiez me\*^me pas. C'est aussi parce qu'il existe de bien meilleures approches pour les nombreux cas ou\*` les programmeurs Perl de\*'butants tentent d'utiliser les re\*'fe\*'rences symboliques, par exemple les hash imbrique\*'s ou les hash de tableaux. Mais il n'est pas interdit d'utiliser les re\*'fe\*'rences symboliques pour manipuler quoi que ce soit d'inte\*'ressant du point de vue de la table des symboles du paquetage, par exemple les noms de me\*'thodes ou les variables de paquetage. En d'autres termes, utilisez des re\*'fe\*'rences de symbole lorsque vous de\*'sirez vous re\*'fe\*'rer a\*` la table des symboles. .PP Il y a plusieurs avantages a\*` confiner tous les attributs de classe en un me\*^me lieu. On peut aise\*'ment les visualiser, les initialiser ou les modifier. Ceci les rend e\*'galement plus facile d'acce\*`s depuis l'exte\*'rieur, par exemple depuis un de\*'bogueur ou un paquetage persistant. Reste un seul proble\*`me, nous n'avons pas automatiquement connaissance du nom de chaque objet d'une classe, me\*^me s'il en a un. Ceci est traite\*' plus loin dans \*(L"Le me\*'ta\-objet e\*'ponyme\*(R". .Sh "A\*` propos de l'he\*'ritage" .IX Subsection "A` propos de l'he'ritage" Supposons que nous instancions une classe de\*'rive\*'e, et que nous acce\*'dons a\*` ses donne\*'es de classe au moyen de l'appel d'une me\*'thode he\*'rite\*'e. Allons-nous re\*'cupe\*'rer les attributs de la classe de base, ou bien ceux de la classe de\*'rive\*'e ? Que se passera-t-il dans les exemples pre\*'ce\*'dents ? La classe de\*'rive\*'e he\*'rite de toutes les me\*'thodes de la classe de base, y compris celles qui acce\*`dent aux attributs de classe. Mais a\*` quel paquetage appartiennent les attributs de classe ? .PP Ainsi que le sugge\*`re leur nom, la re\*'ponse est que les attributs de classe sont stocke\*'s dans le paquetage ou\*` ces me\*'thodes ont e\*'te\*' compile\*'es. Lorsqu'on invoque la me\*'thode &CData1 sur le nom de la classe de\*'rive\*'e ou sur celui d'un objet de cette classe, la version vue pre\*'ce\*'demment est toujours valable, et donc on acce\*'dera a\*` \f(CW$Some_Class::CData1\fR \- ou, dans la version clonant les me\*'thodes, a\*` \&\f(CW$Some_Class::ClassData{CData1}\fR. .PP Il faut garder a\*` l'esprit que ces me\*'thodes de classe s'exe\*'cutent dans le contexte de leur classe de base, et non dans celui de la classe de\*'rive\*'e. Parfois c'est exactement ce que nous voulons. Si Feline est une sous-classe de Carnivore, alors la population mondiale des Carnivore s'accroi\*^t a\*` chaque naissance d'un Feline. Mais qu'en est-il si l'on cherche combien il y a de Feline parmi les Carnivore ? L'approche actuelle ne re\*'pond pas a\*` cette question. .PP Il faut de\*'cider, au cas par cas, si le fait que les attributs de classe soient relatifs au paquetage a un sens. Si c'est votre choix, alors il ne faut plus laisser de co\*^te\*' le premier argument de la fonction. Ce peut e\*^tre aussi bien un nom de paquetage si la me\*'thode a e\*'te\*' invoque\*'e directement sur un nom de paquetage, ou bien une re\*'fe\*'rence d'objet si la me\*'thode a e\*'te\*' invoque\*'e sur une re\*'fe\*'rence d'objet. Auquel cas la fonction \f(CW\*(C`ref()\*(C'\fR renvoie la classe de cet objet. .PP .Vb 9 \& package Some_Class; \& sub CData1 { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& my $varname = $class . "::CData1"; \& no strict "refs"; # pour acce\*'der symboliquement aux donne\*'es du paquetage \& $$varname = shift if @_; \& return $$varname; \& } .Ve .PP Il faut alors faire de me\*^me pour tous les autres attributs de classe (tels que CData2, etc.) pour lesquels vous souhaitez un acce\*`s aux variables du paquetage invoque\*' pluto\*^t qu'un acce\*`s au paquetage compile\*' que nous avions pre\*'ce\*'demment. .PP Une fois de plus nous de\*'sactivons temporairement les re\*'fe\*'rences strictes, faute de quoi nous ne pourrions pas utiliser les noms symboliques pleinement qualifie\*'s pour le paquetage global. Ceci est tre\*`s raisonnable : puisque les variables de paquetage re\*'sident par de\*'finition dans un paquetage, rien n'interdit d'y acce\*'der via la table de symboles de ce paquetage. C'est d'ailleurs la raison de sa pre\*'sence (Bon, c\*,a suffit). .PP Que penser si nous utilisions un simple hash a\*` tout faire, ainsi que des me\*'thodes clone\*'es ? A quoi cela ressemblerait-il ? La seule diffe\*'rence re\*'siderait dans les fermetures utilise\*'es pour ge\*'ne\*'rer de nouvelles entre\*'es de me\*'thodes pour la table de symboles de la classe. .PP .Vb 8 \& no strict "refs"; \& *$datum = sub { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& my $varname = $class . "::ClassData"; \& $varname\->{$datum} = shift if @_; \& return $varname\->{$datum}; \& } .Ve .Sh "Le me\*'ta\-objet e\*'ponyme" .IX Subsection "Le me'ta-objet e'ponyme" N.d.t : e\*'ponyme signifie \*(L"qui donne son nom\*(R". Le me\*'ta\-objet (la classe) \*(L"donne son nom\*(R" au hash qui le repre\*'sente. .PP On pourrait dire que le nom du hash \f(CW%ClassData\fR de l'exemple pre\*'ce\*'dent n'est ni le plus imaginatif ni le plus intuitif. Peut-on trouver quelque chose de plus sense\*', ou de plus utile, voire les deux ? .PP La re\*'ponse est, de fait, positive. Pour un \*(L"me\*'ta\-objet de classe\*(R", nous allons utiliser une variable de paquetage de me\*^me nom que le paquetage lui\-me\*^me. Dans la porte\*'e lexicale des de\*'clarations d'un paquetage \&\f(CW\*(C`Some_Class\*(C'\fR, nous utiliserons le hash de me\*^me nom \f(CW%Some_Class\fR comme me\*'ta\-objet de cette classe. (Utiliser un hash nomme\*' de manie\*`re e\*'ponyme ressemble beaucoup aux classes nommant leurs constructeurs de manie\*`re e\*'ponyme, a\*` la manie\*`re de Python ou de \*(C+. Cela signifie que la classe \f(CW\*(C`Some_Class\*(C'\fR pourrait avoir un constructeur \f(CW\*(C`Some_Class::Some_Class\*(C'\fR, et sans doute exporter ce nom. Si vous cherchez un exemple, c'est ce que fait la classe \&\f(CW\*(C`StrNum\*(C'\fR de la recette 13.14 du \fIPerl Cookbook\fR.) .PP Cette approche pre\*'visible est tre\*`s appre\*'ciable, entre autres parce qu'elle fournit un identificateur connu pour l'aide au de\*'bugging, a\*` la persistance transparente, ou au contro\*^le. C'est e\*'galement le nom e\*'vident pour les classes monadiques et les attributs transparents que nous aborderons plus tard. .PP Voici un exemple d'une telle classe. Remarquez le nom du hash contenant le me\*'ta\-objet, le me\*^me que celui du paquetage utilise\*' pour imple\*'menter la classe. .PP .Vb 2 \& package Some_Class; \& use strict; \& \& # cre\*'er un me\*'ta\-objet de classe en utilisant ces noms si merveilleux \& our %Some_Class = ( # our() est nouveau depuis perl5.6 \& CData1 => "", \& CData2 => "", \& ); \& \& # cette me\*'thode d'acce\*`s est relative au package appelant \& sub CData1 { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& no strict "refs"; # pour acce\*'der au me\*'ta\-objet e\*'ponyme \& $class\->{CData1} = shift if @_; \& return $class\->{CData1}; \& } \& \& # but this accessor is not \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& no strict "refs"; # pour acce\*'der au me\*'ta\-objet e\*'ponyme \& _\|_PACKAGE_\|_ \-> {CData2} = shift if @_; \& return _\|_PACKAGE_\|_ \-> {CData2}; \& } .Ve .PP Dans la deuxie\*`me me\*'thode d'acce\*`s, la notation \f(CW\*(C`_\|_PACKAGE_\|_\*(C'\fR a e\*'te\*' utilise\*'e pour deux raisons. La premie\*`re, pour e\*'viter de coder en dur le nom du paquetage, au cas ou\*` nous de\*'ciderions ulte\*'rieurement de changer ce nom. La deuxie\*`me, pour e\*'clairer le lecteur sur le fait qu'il s'agit du paquetage actuellement compile\*', et non du paquetage de l'objet ou de la classe appelante. Si la longue se\*'quence de caracte\*`res non alphabe\*'tiques vous de\*'range, on peut toujours cre\*'er une variable contenant la chai\*^ne \f(CW\*(C`_\|_PACKAGE_\|_\*(C'\fR. .PP .Vb 7 \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& no strict "refs"; # pour acce\*'der au me\*'ta\-objet e\*'ponyme \& my $class = _\|_PACKAGE_\|_; \& $class\->{CData2} = shift if @_; \& return $class\->{CData2}; \& } .Ve .PP Bien que nous utilisions au mieux les re\*'fe\*'rences symboliques, certains seront de\*'concerte\*'s de voir le test sur les re\*'fe\*'rences strictes aussi souvent de\*'sactive\*'. E\*'tant donne\*' une re\*'fe\*'rence symbolique, on peut toujours en de\*'duire une re\*'fe\*'rence re\*'elle (bien que l'inverse ne soit pas vrai). Aussi nous allons e\*'crire un sous-programme qui re\*'alisera cette conversion pour nous. Appele\*' comme une fonction sans arguments, il renvoie une re\*'fe\*'rence sur le hash e\*'ponyme de la classe compile\*'e. Appele\*' en tant que me\*'thode de classe, il renvoie une re\*'fe\*'rence sur le hash e\*'ponyme du code appelant. Enfin, appele\*' en tant que me\*'thode d'objet, il renvoie une re\*'fe\*'rence vers le hash e\*'ponyme de toute classe a\*` laquelle appartient l'objet. .PP .Vb 2 \& package Some_Class; \& use strict; \& \& our %Some_Class = ( # our() est nouveau depuis perl5.6 \& CData1 => "", \& CData2 => "", \& ); \& \& # ce sous\-programme a trois faces : fonction, me\*'thode de classe, ou me\*'thode d'objet \& sub _classobj { \& my $obclass = shift || _\|_PACKAGE_\|_; \& my $class = ref($obclass) || $obclass; \& no strict "refs"; # pour convertir les re\*'fe\*'rences symboliques en re\*'fe\*'rences re\*'elles \& return \e%$class; \& } \& \& for my $datum (keys %{ _classobj() } ) { \& # inhiber "strict refs" pour pouvoir \& # enregistrer une me\*'thode dans la table des symboles \& no strict "refs"; \& *$datum = sub { \& use strict "refs"; \& my $self = shift\->_classobj(); \& $self\->{$datum} = shift if @_; \& return $self\->{$datum}; \& } \& } .Ve .Sh "Re\*'fe\*'rences indirectes aux donne\*'es de classe" .IX Subsection "Re'fe'rences indirectes aux donne'es de classe" Une strate\*'gie classique et raisonnable pour se souvenir des attributs de classe est de garder une re\*'fe\*'rence de chaque variable de paquetage dans l'objet lui\-me\*^me. C'est une strate\*'gie que vous avez probablement de\*'ja\*` rencontre\*'e, par exemple dans perltoot et perlbot, mais voici quelques variations auxquelles vous n'auriez sans doute pas pense\*' auparavant. .PP .Vb 2 \& package Some_Class; \& our($CData1, $CData2); # our() est nouveau depuis perl5.6 \& \& sub new { \& my $obclass = shift; \& return bless my $self = { \& ObData1 => "", \& ObData2 => "", \& CData1 => \e$CData1, \& CData2 => \e$CData2, \& } => (ref $obclass || $obclass); \& } \& \& sub ObData1 { \& my $self = shift; \& $self\->{ObData1} = shift if @_; \& return $self\->{ObData1}; \& } \& \& sub ObData2 { \& my $self = shift; \& $self\->{ObData2} = shift if @_; \& return $self\->{ObData2}; \& } \& \& sub CData1 { \& my $self = shift; \& my $dataref = ref $self \& ? $self\->{CData1} \& : \e$CData1; \& $$dataref = shift if @_; \& return $$dataref; \& } \& \& sub CData2 { \& my $self = shift; \& my $dataref = ref $self \& ? $self\->{CData2} \& : \e$CData2; \& $$dataref = shift if @_; \& return $$dataref; \& } .Ve .PP Comme vous le voyez ci\-dessus, une classe de\*'rive\*'e he\*'ritera de ces me\*'thodes, et pourra par conse\*'quent acce\*'der aux variables de paquetage dans le paquetage de la classe de base. Ce n'est pas ne\*'cessairement le comportement souhaite\*' dans tous les cas. Voici un exemple utilisant un me\*'ta\-objet variable, prenant soin d'acce\*'der aux donne\*'es du bon paquetage. .PP .Vb 2 \& package Some_Class; \& use strict; \& \& our %Some_Class = ( # our() est nouveau depuis perl5.6 \& CData1 => "", \& CData2 => "", \& ); \& \& sub _classobj { \& my $self = shift; \& my $class = ref($self) || $self; \& no strict "refs"; \& # prendre une re\*'fe\*'rence (hard) sur le meta\-object e\*'ponyme \& return \e%$class; \& } \& \& sub new { \& my $obclass = shift; \& my $classobj = $obclass\->_classobj(); \& bless my $self = { \& ObData1 => "", \& ObData2 => "", \& CData1 => \e$classobj\->{CData1}, \& CData2 => \e$classobj\->{CData2}, \& } => (ref $obclass || $obclass); \& return $self; \& } \& \& sub ObData1 { \& my $self = shift; \& $self\->{ObData1} = shift if @_; \& return $self\->{ObData1}; \& } \& \& sub ObData2 { \& my $self = shift; \& $self\->{ObData2} = shift if @_; \& return $self\->{ObData2}; \& } \& \& sub CData1 { \& my $self = shift; \& $self = $self\->_classobj() unless ref $self; \& my $dataref = $self\->{CData1}; \& $$dataref = shift if @_; \& return $$dataref; \& } \& \& sub CData2 { \& my $self = shift; \& $self = $self\->_classobj() unless ref $self; \& my $dataref = $self\->{CData2}; \& $$dataref = shift if @_; \& return $$dataref; \& } .Ve .PP Le fait d'utiliser un me\*'ta\-objet e\*'ponyme semble rendre le code plus propre, en plus de rendre l'utilisation de \f(CW\*(C`strict refs\*(C'\fR plus claire. A l'inverse de la version pre\*'ce\*'dente, cette dernie\*`re nous montre quelque chose d'inte\*'ressant par rapport a\*` l'he\*'ritage : elle acce\*`de au me\*'ta\-objet de classe de la classe appelante pluto\*^t qu'a\*` celui appartenant a\*` la classe dans laquelle la me\*'thode a e\*'te\*' initialement compile\*'e. .PP Il est facile d'acce\*'der aux donne\*'es du me\*'ta\-objet de classe, et de visualiser l'e\*'tat de la classe dans son ensemble, en utilisant un me\*'canisme externe similaire a\*` ceux utilise\*'s dans les debugger ou lorsqu'on imple\*'mente une classe persistante. Cela fonctionne parce que le me\*'ta\-objet de classe est une variable de paquetage, posse\*`de un nom connu de tous, et regroupe toutes ses donne\*'es. (La persistance transparente n'est pas toujours re\*'alisable, mais c'est certainement une ide\*'e attirante.) .PP Il n'y a pas de contro\*^le ve\*'rifiant si les me\*'thodes d'acce\*`s aux objets ont e\*'te\*' invoque\*'es sur un nom de classe. Si le pragma \f(CW\*(C`strict ref\*(C'\fR est active\*', ce proble\*`me n'existe pas. Sinon, vous obtiendrez le me\*'ta\-objet e\*'ponyme. Ce que vous faites avec \- ou a\*` partir de \- lui, c'est votre affaire. Les deux prochaines sections montrent de nouvelles utilisations de cette puissante caracte\*'ristique. .Sh "Les classes univalentes" .IX Subsection "Les classes univalentes" Un certain nombre de modules standards livre\*'s avec Perl fournissent des interfaces de classe de\*'pourvus de me\*'thodes d'attribut. Le module le plus commune\*'ment utilise\*' parmi les pragma, le module Exporter, est une classe sans constructeur ni attributs. Son travail consiste simplement a\*` fournir une interface standard pour les modules de\*'sireux d'exporter une partie de leur espace de nom dans celui de l'appelant. Les modules utilisent la me\*'thode \&\f(CW&import\fR d'Exporter simplement en ajoutant la mention \*(L"Exporter\*(R" dans \f(CW@ISA\fR, le tableau de leur paquetage de\*'crivant la liste des ance\*^tres. Cependant la classe \*(L"Exporter\*(R" ne fournit pas de constructeur, on ne peut donc avoir plusieurs instances de cette classe. En fait, on ne peut en avoir aucune \&\- cela n'aurait pas de sens. Nous n'avons acce\*`s qu'a\*` ses me\*'thodes. L'interface ne contient pas d'information d'e\*'tat, aussi les donne\*'es d'e\*'tat sont totalement superflues. .PP On voit de temps en temps une classe qui n'accepte qu'une instance unique. Ce type de classe s'appelle une \fIclasse univalente\fR, ou, moins formellement, un \fIsingleton\fR ou une \fIclasse des hautes-terres\fR. .PP Ou\*` ranger l'e\*'tat, les attributs d'une classe monadique ? Comment e\*^tre certain qu'il n'existera jamais plus d'une instance ? La\*` ou\*` l'on pourrait simplement utiliser un groupe de variables de paquetage, il est tout de me\*^me plus propre d'utiliser le hash du me\*^me nom. Voici un exemple complet de classe monadique : .PP .Vb 2 \& package Cosmos; \& %Cosmos = (); \& \& # me\*'thode d'acce\*`s pour l'attribut "name" \& sub name { \& my $self = shift; \& $self\->{name} = shift if @_; \& return $self\->{name}; \& } \& \& # me\*'thode d'acce\*`s en lecture seulement pour l'attribut "birthday" \& sub birthday { \& my $self = shift; \& die "impossible de modifier la date de naissance" if @_; # XXX: croak() serait mieux \& return $self\->{birthday}; \& } \& \& # me\*'thode d'acce\*`s pour l'attribut "stars" \& sub stars { \& my $self = shift; \& $self\->{stars} = shift if @_; \& return $self\->{stars}; \& } \& \& # rogneugneu ! \- une de nos e\*'toiles vient de se faire la belle ! \& sub supernova { \& my $self = shift; \& my $count = $self\->stars(); \& $self\->stars($count \- 1) if $count > 0; \& } \& \& # me\*'thode constructeur/initialiseur \- rede\*'marrage \& sub bigbang { \& my $self = shift; \& %$self = ( \& name => "the world according to tchrist", \& birthday => time(), \& stars => 0, \& ); \& return $self; # oui, il s'agit probablement d'une classe. SURPRISE ! \& } \& \& # Apre\*`s que la classe ait e\*'te\*' compile\*'e, mais avant le retour d'un quelconque \& # use ou require, de\*'marrage de l'univers par un bang. \& _\|_PACKAGE_\|_ \-> bigbang(); .Ve .PP Un instant : ceci n'a pas l'air bien extraordinaire. Ces accesseurs d'attributs de classe univalente ne semblent pas diffe\*'rents de ceux d'une classe habituelle. Le point important est que rien n'indique que \f(CW$self\fR doit e\*^tre une re\*'fe\*'rence a\*` un objet consacre\*'. Simplement ce doit e\*^tre quelque chose sur lequel on peut invoquer des me\*'thodes. Ici le nom du paquetage lui\-me\*^me, \&\f(CW\*(C`Cosmos\*(C'\fR, fonctionne comme un objet. Regardez la me\*'thode \f(CW&supernova\fR. Est-ce une me\*'thode de classe ou une me\*'thode d'objet ? L'analyse statique ne peut pas donner la re\*'ponse. Perl ne s'inte\*'resse pas a\*` cela, et c'est ce que vous devriez faire e\*'galement. Dans les trois me\*'thodes d'attributs, \f(CW%$self\fR acce\*`de vraiment aux variables de paquetage de \f(CW%Cosmos\fR. .PP Si, comme Stephen Hawking, vous e\*'rigez en principe l'existence d'univers multiples, se\*'quentiels et sans aucun rapport entre eux, alors vous pouvez invoquer la me\*'thode \f(CW&bigbang\fR a\*` n'importe quel moment afin de tout recommencer. Vous pourriez penser a\*` la me\*'thode \f(CW&bigbang\fR davantage comme a\*` un initialiseur qu'a\*` un constructeur, dans la mesure ou\*` la fonction n'alloue pas de me\*'moire supple\*'mentaire ; elle initialise simplement tout ce qui existe de\*'ja\*`. Seulement, comme n'importe quel autre constructeur, elle retourne une valeur scalaire que l'on utilisera ulte\*'rieurement dans les invocations de me\*'thodes. .PP Supposez que quelque temps apre\*`s vous de\*'cidez qu'un seul univers n'est pas suffisant. Vous pourriez re\*'e\*'crire entie\*`rement une nouvelle classe, or vous posse\*'dez de\*'ja\*` une classe qui fait tout ce que voulez \- sauf qu'elle est univalente, et que vous voulez plusieurs cosmos. .PP La re\*'utilisation du code via l'he\*'ritage de classe n'est pas autre chose. Regardez la taille du nouveau code : .PP .Vb 3 \& package Multiverse; \& use Cosmos; \& @ISA = qw(Cosmos); \& \& sub new { \& my $protoverse = shift; \& my $class = ref($protoverse) || $protoverse; \& my $self = {}; \& return bless($self, $class)\->bigbang(); \& } \& 1; .Ve .PP Ayant soigneusement de\*'fini la classe \f(CW\*(C`Cosmos\*(C'\fR lors de sa cre\*'ation, nous pouvons le moment venu la re\*'utiliser sans toucher a\*` une seule ligne de code afin d'e\*'crire notre classe Multiverse. Le code qui fonctionnait lorsqu'on l'invoquait en tant que me\*'thode de classe continue a\*` fonctionner parfaitement lorsqu'il est invoque\*' sur chacune des instances de la classe de\*'rive\*'e. .PP On peut s'e\*'tonner que dans la classe \f(CW\*(C`Cosmos\*(C'\fR ci-dessus la valeur retourne\*'e par le \*(L"constructeur\*(R" \f(CW&bigbang\fR ne soit pas une re\*'fe\*'rence a\*` un objet consacre\*'. C'est tout simplement le propre nom de la classe. Un nom de classe est de fait un objet parfaitement acceptable. Il a un e\*'tat, une signification, une identite\*', les trois attributs cruciaux d'un syste\*`me objet. Il ouvre la porte a\*` l'he\*'ritage, au polymorphisme et a\*` l'encapsulation. Que demander de plus a\*` un objet ? .PP Pour comprendre l'orientation objet en Perl, il est important de reconnai\*^tre l'unification en simple me\*'thodes de ce que d'autres langages pourraient appeler \*(L"me\*'thodes de classe\*(R" et \*(L"me\*'thodes d'objet\*(R". Les \*(L"me\*'thodes de classe\*(R" et les \*(L"me\*'thodes d'objet\*(R" sont diffe\*'rencie\*'es uniquement dans l'esprit du programmeur Perl, mais non dans le langage Perl lui\-me\*^me. .PP Ceci e\*'tant, un constructeur n'a pas de caracte\*'ristiques spe\*'ciales, et c'est une des raisons pour lesquelles Perl n'a pas de mot\-cle\*' pour le de\*'signer. Un \*(L"constructeur\*(R" est simplement un terme informel utilise\*' vaguement pour de\*'crire une me\*'thode retournant une valeur scalaire sur laquelle on pourra ensuite appliquer des appels de me\*'thode. Il suffit de savoir que cette valeur scalaire peut aussi bien e\*^tre un nom de classe qu'une re\*'fe\*'rence d'objet. Il n'y a aucune raison pour qu'elle soit une re\*'fe\*'rence sur un objet flambant neuf. .PP Il peut exister autant \- ou aussi peu \- de constructeurs que l'on veut, et leur nom importe peu. Nommer aveugle\*'ment et docilement \f(CW\*(C`new()\*(C'\fR chaque nouveau constructeur que l'on e\*'crit signifie que l'on parle le Perl avec un fort accent \*(C+, ce qui dessert les deux langages. Il n'y a aucune raison pour que chaque classe n'ait qu'un constructeur, ou bien pour qu'un constructeur s'appelle \fInew()\fR, ou encore qu'un constructeur soit utilise\*' en tant que me\*'thode de classe et non en tant que me\*'thode d'objet. .PP La section suivante montre l'utilite\*' de prendre de la distance par rapport a\*` la distinction formelle entre les appels aux me\*'thodes de classe et ceux des me\*'thodes d'objet, aussi bien dans les constructeurs que dans les me\*'thodes d'acce\*`s. .Sh "Les attributs transparents" .IX Subsection "Les attributs transparents" Le ro\*^le du hash e\*'ponyme de paquetage ne se re\*'sume pas uniquement a\*` recueillir une classe ainsi que des donne\*'es d'e\*'tat globales. Il peut aussi repre\*'senter une sorte de mode\*`le contenant les valeurs initiales par de\*'faut des attributs d'objet. Ces valeurs initiales peuvent alors e\*^tre utilise\*'es dans les constructeurs pour initialiser un objet particulier. Il peut aussi servir a\*` imple\*'menter les attributs transparents. Un attribut transparent posse\*`de une valeur par de\*'faut valable pour toute la classe. Chaque objet peut lui donner une valeur, et dans ce cas \f(CW\*(C`$object\-\*(C'\fR\fIattribute()\fR> retournera cette valeur. Mais si la valeur n'a pas e\*'te\*' initialise\*'e, \f(CW\*(C`$object\-\*(C'\fR\fIattribute()\fR> retournera la valeur par de\*'faut pour la classe. .PP Ce comportement nous permet d'avoir une approche du type \*(L"copie\-si\-e\*'criture\*(R" pour ces attributs transparents. Si l'on se contente de lire leur valeur, tout reste transparent. Mais si on modifie leur valeur, cette nouvelle valeur n'est valable que pour l'objet courant. D'autre part, si l'on utilise la classe en tant qu'objet et qu'on y modifie directement la valeur d'un attribut, la valeur du me\*'ta\-objet sera modifie\*'e, et par suite des lectures ulte\*'rieures sur les objets qui n'auront pas initialise\*' ces attributs retourneront les nouvelles valeurs du me\*'ta\-objet. Tandis que les objets qui auront initialise\*' la valeur de l'attribut ne verront aucun changement. .PP Voyons un exemple concret utilisant cette fonctionnalite\*' avant de monter la manie\*`re d'imple\*'menter ces attributs. Soit une classe de nom \f(CW\*(C`Some_Class\*(C'\fR posse\*'dant un attribut transparent nomme\*' \f(CW\*(C`color\*(C'\fR. Initialisons d'abord la couleur dans le me\*'ta\-objet, puis cre\*'ons trois objets au moyen d'un constructeur nomme\*' par exemple \f(CW&spawn\fR. .PP .Vb 2 \& use Vermin; \& Vermin\->color("vermilion"); \& \& $ob1 = Vermin\->spawn(); # c'est ainsi qu'arriva le Jedi \& $ob2 = Vermin\->spawn(); \& $ob3 = Vermin\->spawn(); \& \& print $obj3\->color(); # affiche "vermilion" .Ve .PP Chacun de ces objets est maintenant de couleur \*(L"vermilion\*(R", parce que \&\*(L"vermilion\*(R" est la valeur de cet attribut dans le me\*'ta\-objet, mais ces objets ne posse\*`dent pas de couleur individuelle initialise\*'e. .PP Si l'on modifie la valeur d'un objet, cela n'a aucun effet sur les autres objets cre\*'e\*'s auparavant. .PP .Vb 3 \& $ob3\->color("chartreuse"); \& print $ob3\->color(); # affiche "chartreuse" \& print $ob1\->color(); # affiche "vermilion", de manie\*`re transparente .Ve .PP Si maintenant on utilise \f(CW$ob3\fR pour cre\*'er un autre objet, le nouvel objet he\*'ritera de sa couleur, qui sera maintenant \*(L"chartreuse\*(R". La raison de ceci est que le constructeur utilise l'objet invoque\*' comme mode\*`le pour les initialisations d'attributs. Lorsque l'objet invoque\*' est le nom de classe, l'objet utilise\*' comme mode\*`le est le me\*'ta\-objet e\*'ponyme. Lorsque l'objet invoque\*' est une re\*'fe\*'rence sur un objet instancie\*', le constructeur \f(CW&spawn\fR se sert de lui comme mode\*`le. .PP .Vb 2 \& $ob4 = $ob3\->spawn(); # $ob3 est maintenant un mode\*`le, et non %Vermin \& print $ob4\->color(); # affiche "chartreuse" .Ve .PP Toute valeur re\*'elle initialise\*'e dans l'objet mode\*`le sera copie\*'e dans le nouvel objet. Mais les attributs non de\*'finis dans l'objet mode\*`le, transparents par de\*'finition, le resteront dans le nouvel objet. .PP Modifions maintenant l'attribut de couleur de l'ensemble de la classe : .PP .Vb 5 \& Vermin\->color("azure"); \& print $ob1\->color(); # affiche "azure" \& print $ob2\->color(); # affiche "azure" \& print $ob3\->color(); # affiche "chartreuse" \& print $ob4\->color(); # affiche "chartreuse" .Ve .PP Ce changement de couleur n'a d'effet que pour les deux premiers objets, ceux qui e\*'taient transparents lors d'un acce\*`s aux valeurs du me\*'ta\-objet. Les couleurs des deux suivants e\*'taient de\*'ja\*` initialise\*'es, et par conse\*'quent ne changent pas. .PP Reste une question importante. Les modifications du me\*'ta\-objet sont propage\*'es dans les attributs transparents de la classe entie\*`re, mais qu'en est-il des modifications effectue\*'es sur les objets particuliers ? Si l'on modifie la couleur de \f(CW$ob3\fR, celle de \f(CW$ob4\fR est-elle modifie\*'e ? Ou, inversement, si l'on modifie celle de \f(CW$ob4\fR, celle de \f(CW$ob3\fR s'en trouve-t-elle modifie\*'e ? .PP .Vb 3 \& $ob3\->color("amethyst"); \& print $ob3\->color(); # affiche "amethyst" \& print $ob4\->color(); # hmm: "chartreuse" ou "amethyst"? .Ve .PP Il ne faut pas faire cela, bien que certains affirment que nous le devrions dans certaines situations assez rares. La re\*'ponse a\*` la question pose\*'e dans le commentaire ci\-dessus, mis a\*` part le bon gou\*^t, doit e\*^tre \*(L"chartreuse\*(R" et non \&\*(L"amethyst\*(R". Ainsi nous devrons traiter ces attributs un peu a\*` la manie\*`re dont les attributs de processus (tels que les variables d'environnement, les identificateurs d'utilisateur ou de groupe, ou encore le re\*'pertoire courant) le sont lors d'un \f(CW\*(C`fork()\*(C'\fR. Vous seul pouvez les modifier, mais gardez a\*` l'esprit que ces modifications se retrouveront dans les fils a\*` nai\*^tre. Les modifications d'un objet ne seront jamais re\*'percute\*'es vers leur parent ni vers d'e\*'ventuels objets fils existants. Toutefois ces modifications s'appliqueront a\*` de tels objets cre\*'e\*'s ulte\*'rieurement. .PP Que faire pour un objet posse\*'dant des valeurs re\*'elles pour ses attributs, et dont on veut rendre les valeurs d'attribut d'objet a\*` nouveau transparentes ? Nous allons de\*'finir la classe de manie\*`re que cet attribut redevienne transparent lorsqu'on invoque une me\*'thode d'acce\*`s avec un argument \f(CW\*(C`undef\*(C'\fR. .PP .Vb 1 \& $ob4\->color(undef); # retour vers "azure" .Ve .PP Voici l'imple\*'mentation comple\*`te de Vermin telle que de\*'crite ci\-dessus. .PP .Vb 1 \& package Vermin; \& \& # voici le me\*'ta\-objet de classe, nomme\*' eponymement. \& # il contient tous les attributs de classe, ainsi que tous les attributs d'instances \& # c'est ainsi que la suite peut e\*^tre utilise\*'e pour les deux initialisations \& # et la transparence. \& \& our %Vermin = ( # our() est nouveau depuis perl5.6 \& PopCount => 0, # majuscules pour un attribut de classe \& color => "beige", # minuscules pour un attribut d'instance \& ); \& \& # me\*'thode "constructeur" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub spawn { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& my $self = {}; \& bless($self, $class); \& $class\->{PopCount}++; \& # initialiser les champs a\*` partir de l'objet appelant, ou les omettre \& # si l'objet appelant est la classe, afin de fournir la transparence \& %$self = %$obclass if ref $obclass; \& return $self; \& } \& \& # me\*'thode d'acce\*`s transparente pour l'attribut "color" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub color { \& my $self = shift; \& my $class = ref($self) || $self; \& \& # prend en charge l'invocation de classe \& unless (ref $self) { \& $class\->{color} = shift if @_; \& return $class\->{color} \& } \& \& # prend en charge l'invocation depuis un objet \& $self\->{color} = shift if @_; \& if (defined $self\->{color}) { # not exists! \& return $self\->{color}; \& } else { \& return $class\->{color}; \& } \& } \& \& # methode d'acce\*`s pour l'attribut de classe "PopCount" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& # mais utilise l'objet uniquement pour repe\*'rer le me\*'ta\-objet \& sub population { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& return $class\->{PopCount}; \& } \& \& # destructeur d'instance \& # invoque\*' uniquement en tant que me\*'thode d'objet \& sub DESTROY { \& my $self = shift; \& my $class = ref $self; \& $class\->{PopCount}\-\-; \& } .Ve .PP Voici une paire de me\*'thodes d'aide assez pratiques. Ce ne sont nullement des me\*'thodes d'acce\*`s. Elles sont utilise\*'es pour de\*'tecter l'accessibilite\*' des attributs. La me\*'thode \f(CW&is_translucent\fR de\*'termine si un attribut d'un objet particulier provient du me\*'ta\-objet. La me\*'thode \f(CW&has_attribute\fR de\*'tecte si une classe imple\*'mente une proprie\*'te\*' particulie\*`re. Elle peut e\*'galement servir a\*` de\*'terminer si une proprie\*'te\*' est inde\*'finie ou si elle n'existe pas. .PP .Vb 6 \& # de\*'termine si un atribut d'objet est transparent \& # (typiquement ?) invoque\*'e comme une me\*'thode d'objet \& sub is_translucent { \& my($self, $attr) = @_; \& return !defined $self\->{$attr}; \& } \& \& # teste la pre\*'sence d'un attribut dans la classe \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub has_attribute { \& my($self, $attr) = @_; \& my $class = ref $self if $self; \& return exists $class\->{$attr}; \& } .Ve .PP Si l'on pre\*'fe\*`re installer les me\*'thodes d'acce\*`s de manie\*`re plus ge\*'ne\*'rique, on peut se servir de la convention majuscules vs minuscules pour enregistrer dans le paquetage les me\*'thodes approprie\*'es clone\*'es depuis les fermetures ge\*'ne\*'riques. .PP .Vb 10 \& for my $datum (keys %{ +_\|_PACKAGE_\|_ }) { \& *$datum = ($datum =~ /^[A\-Z]/) \& ? sub { # installe la me\*'thode d'acce\*`s de classe \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& return $class\->{$datum}; \& } \& : sub { # installe la me\*'thode d'acce\*`s transparente \& my $self = shift; \& my $class = ref($self) || $self; \& unless (ref $self) { \& $class\->{$datum} = shift if @_; \& return $class\->{$datum} \& } \& $self\->{$datum} = shift if @_; \& return defined $self\->{$datum} \& ? $self \-> {$datum} \& : $class \-> {$datum} \& } \& } .Ve .PP En guise d'exercice, nous proposons au lecteur de traduire en \*(C+, Java et Python cette approche base\*'e sur les fermetures. N'oubliez pas de me pre\*'venir par mail de\*`s que vous aurez re\*'alise\*' cela. .SH "Donne\*'es de classe en tant que variables lexicales" .IX Header "Donne'es de classe en tant que variables lexicales" .Sh "Domaine prive\*' et responsabilite\*'" .IX Subsection "Domaine prive' et responsabilite'" Dans les exemples qui pre\*'ce\*`dent, a\*` l'inverse des convention utilise\*'es par certains programmeurs Perl, nous n'avons pas pre\*'fixe\*' au moyen d'un caracte\*`re souligne\*' les variables de paquetage servant d'attribut de classe, pas davantage que les noms de cle\*'s utilise\*'es pour les attributs d'instance. Nous n'avons pas besoin de ces marqueurs pour sugge\*'rer une proprie\*'te\*' de fait sur les variables d'attributs ou les cle\*'s de hash, parce qu'elles sont toujours notoirement prive\*'es ! Le monde exte\*'rieur n'a pas a\*` jouer avec quoi que ce soit dont le moyen d'acce\*`s a e\*'te\*' publie\*' par une classe au travers d'une interface documente\*'e ; en d'autres termes, au moyen des invocations de me\*'thode. Et pas davantage au moyen de n'importe quelle me\*'thode. Les me\*'thodes dont le nom commence par un souligne\*' sont traditionnellement conside\*'re\*'es comme e\*'tant a\*` la limite exte\*'rieure de la classe. Si des e\*'trangers ignore l'interface des me\*'thodes documente\*'es et joue avec les variables internes de votre classe de sorte qu'a\*` la fin quelque chose casse, ce n'est pas votre faute \- c'est la leur. .PP Perl pre\*'fe\*`re la responsabilite\*' individuelle au contro\*^le impose\*'. Perl vous respecte suffisamment pour vous laisser choisir votre niveau pre\*'fe\*'re\*' de peine ou de plaisir. Perl suppose que vous e\*^tes cre\*'atif, intelligent, capable de prendre vos propres de\*'cisions \- et attend de vous que vous preniez l'entie\*`re responsabilite\*' de vos actions. Dans un monde parfait, ces remontrances devraient e\*^tre suffisantes, et tout le monde serait intelligent, responsable, heureux, et cre\*'atif. Et soigneux. Il ne faut surtout pas oublier d'e\*^tre soigneux, mais il est quelque peu difficile d'espe\*'rer cela. Me\*^me Einstein peut prendre accidentellement un mauvais virage et se retrouver finalement perdu dans un quartier inconnu de la ville. .PP Certains deviennent paranoi\*:aques a\*` la vue de variables de paquetage expose\*'es a\*` la vue de tous ceux qui pourraient les atteindre et les alte\*'rer. Certains vivent dans la crainte constante que n'importe qui puisse re\*'aliser une me\*'chancete\*' n'importe ou\*`. La solution a\*` ce proble\*`me est bien entendu de tuer le me\*'chant. Malheureusement ce n'est pas aussi simple que cela. Ces personnes pre\*'cautionneuses sont e\*'galement effraye\*'es par le fait qu'elles\-me\*^mes ou d'autres puissent re\*'aliser cela non par me\*'chancete\*' mais par manque de soin, aussi bien par accident que par de\*'sespoir. Mais si l'on punit tous les maladroits, tre\*`s rapidement plus personne ne pourra re\*'aliser quoi que ce soit. .PP Si la paranoi\*:a ou les pre\*'cautions sont inutiles, ce manque de facilite\*' peut repre\*'senter un proble\*`me pour certains. Celui-ci peut e\*^tre alle\*'ge\*' si l'on prend l'option de stocker les attributs de classe en tant que variables lexicales pluto\*^t qu'en tant que variables de paquetage. L'ope\*'rateur \f(CW\*(C`my()\*(C'\fR est la source de toute proprie\*'te\*' en Perl, et c'est en effet une forme de proprie\*'te\*' tre\*`s puissante. .PP Il est bien connu, et cela a souvent e\*'te\*' e\*'crit, que Perl ne permet pas de cacher les variables, ce qui n'offre aucune intimite\*' ni aucun isolement au concepteur de classe, et au lieu de cela simplement un fragile ramassis de conventions sociales. Il est facile de prouver que cette perception est fausse. Dans la section suivante, nous montrerons comment imple\*'menter des formes de proprie\*'te\*' aussi puissantes que celles fournies dans la majorite\*' des autres langages oriente\*'s\-objet. .Sh "Variables lexicales dans la porte\*'e du fichier" .IX Subsection "Variables lexicales dans la porte'e du fichier" Une variable lexicale n'est visible que jusqu'a\*` la fin de sa porte\*'e statique. Cela signifie que le seul code capable d'y acce\*'der est celui qui re\*'side textuellement apre\*`s l'ope\*'rateur \f(CW\*(C`my()\*(C'\fR et avant la fin du bloc courant s'il existe, ou jusqu'a\*` la fin du fichier courant s'il n'existe pas. .PP En repartant de l'exemple le plus simple pre\*'sente\*' au de\*'but de ce document, remplac\*,ons les variables \f(CW\*(C`our()\*(C'\fR par leur version \f(CW\*(C`my()\*(C'\fR. .PP .Vb 12 \& package Some_Class; \& my($CData1, $CData2); # la porte\*'e est le fichier, et dans un quelconque paquetage \& sub CData1 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $CData1 = shift if @_; \& return $CData1; \& } \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $CData2 = shift if @_; \& return $CData2; \& } .Ve .PP Assez de cette ancienne variable de paquetage \f(CW$Some_Class::CData1\fR et de ses fre\*`res ! Elles ont maintenant disparu, remplace\*'es par des lexicales. Personne, en dehors de la porte\*'e de la classe, ne peut les atteindre ni modifier l'e\*'tat de la classe autrement qu'en utilisant l'interface documente\*'e. Me\*^me les sous-classes ou les superclasses de celle-ci n'ont pas d'acce\*`s direct a\*` \f(CW$CData1\fR. Elles doivent invoquer les me\*'thodes d'acce\*`s a\*` \f(CW$CData1\fR sur \f(CW\*(C`Some_Class\*(C'\fR ou sur une instance de cette classe, exactement comme tout le monde. .PP Pour e\*^tre tout a\*` fait honne\*^te, la dernie\*`re affirmation suppose que l'on n'a pas place\*' les paquetages de diverses classes dans le me\*^me fichier, ni imple\*'mente\*' la classe dans plusieurs fichiers diffe\*'rents. L'accessibilite\*' de ces variables est base\*'e uniquement sur la porte\*'e statique du fichier. Elle n'a rien a\*` voir avec le paquetage. Cela signifie concre\*`tement que le code appartenant a\*` la me\*^me classe mais se trouvant dans un fichier diffe\*'rent n'a pas d'acce\*`s a\*` ces variables, alors que le code situe\*' dans le me\*^me fichier mais appartenant a\*` un autre paquetage (une autre classe) le peut. C'est pourquoi on sugge\*`re habituellement de mettre chaque module, classe, ou paquetage dans un seul fichier. Vous n'e\*^tes pas oblige\*' de suivre cette suggestion si vous savez vraiment ce que vous faites, cependant vous pourriez parfois vous emme\*^ler les pinceaux, particulie\*`rement au de\*'but. .PP Vous e\*^tes parfaitement libres de grouper vos attributs de classe dans une structure composite de porte\*'e lexicale. .PP .Vb 10 \& package Some_Class; \& my %ClassData = ( \& CData1 => "", \& CData2 => "", \& ); \& sub CData1 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $ClassData{CData1} = shift if @_; \& return $ClassData{CData1}; \& } \& sub CData2 { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $ClassData{CData2} = shift if @_; \& return $ClassData{CData2}; \& } .Ve .PP Afin de simplifier les choses lorsqu'on ajoute d'autres attributs de classe, on peut cre\*'er des me\*'thodes d'acce\*`s en enregistrant des fermetures dans la table de symboles du paquetage. .PP .Vb 10 \& package Some_Class; \& my %ClassData = ( \& CData1 => "", \& CData2 => "", \& ); \& for my $datum (keys %ClassData) { \& no strict "refs"; \& *$datum = sub { \& shift; # XXX: ignorer l'appel sur une classe/objet \& $ClassData{$datum} = shift if @_; \& return $ClassData{$datum}; \& }; \& } .Ve .PP C'est certainement une bonne chose que d'obliger sa propre classe a\*` utiliser les me\*'thodes d'acce\*`s comme tout un chacun. Une meilleure est d'exiger et de s'attendre a\*` ce que tous, y compris les sous-classes et les super\-classes, les amis ou les ennemis, fassent de me\*^me. C'est un point critique du mode\*`le. Il ne faut plus penser en termes de donne\*'es \*(L"publiques\*(R" ou \*(L"prote\*'ge\*'es\*(R", notions se\*'duisantes mais finalement destructrices. Les deux se retourneront contre vous. La raison en est que de\*`s que vous ferez un pas hors de la position stable ou\*` tout est comple\*`tement prive\*', excepte\*' dans la perspective des me\*'thodes d'acce\*`s, vous aurez viole\*' l'enveloppe. Et, ayant ouvert la boi\*^te de Pandore de cette enveloppe d'encapsulation, vous en paierez sans nul doute le prix lorsque de futures modifications de l'imple\*'mentation rendront inope\*'rant un code qui n'a rien a\*` voir avec cela. Si l'on pense que ce n'e\*'tait pas votre objectif lorsque vous avez de\*'cide\*' de passer a\*` l'oriente\*'\-objet, quitte a\*` e\*^tre bride\*' et a\*` subir les fle\*`ches d'une abstraction obse\*'quieuse, une telle rupture semble extre\*^mement malheureuse. .Sh "Retour sur l'he\*'ritage" .IX Subsection "Retour sur l'he'ritage" Supposons que l'on de\*'rive \f(CW\*(C`Another_Class\*(C'\fR a\*` partir de la classe de base \&\f(CW\*(C`Some_Class\*(C'\fR. Qu'obtient\-on en invoquant une des me\*'thodes \f(CW&CData\fR sur la classe de\*'rive\*'e ou sur un objet de cette classe ? La classe de\*'rive\*'e aura-t-elle son propre e\*'tat, ou bien he\*'ritera\-t\-elle de la version de la classe de base des attributs de classe ? .PP La re\*'ponse se trouve dans l'expose\*' ci-dessus : la classe de\*'rive\*'e n'aura \fBpas\fR ses propres donne\*'es d'e\*'tat. Ainsi que pre\*'ce\*'demment, on peut conside\*'rer cela comme une bonne ou une mauvaise chose en fonction de la se\*'mantique de la classe en question. .PP Le moyen le plus propre, le plus sain et le plus simple que posse\*`de une classe de\*'rive\*'e pour se re\*'fe\*'rer a\*` son e\*'tat dans sa porte\*'e lexicale est de surcharger la me\*'thode de la classe de base qui acce\*`de aux attributs de classe. Puisque la me\*'thode re\*'ellement appele\*'e est celle d'un objet de la classe de\*'rive\*'e si ce dernier existe, on obtient automatiquement l'e\*'tat de la classe de\*'rive\*'e de cette manie\*`re. Il faut refouler de manie\*`re e\*'nergique toute envie de fournir une me\*'thode non publie\*'e destine\*'e a\*` obtenir une re\*'fe\*'rence sur le hash \f(CW%ClassData\fR. .PP Tout comme pour d'autres surcharges de me\*'thode, l'imple\*'mentation dans la classe de\*'rive\*'e conserve la possibilite\*' d'invoquer la version de la classe de base en plus de la sienne. En voici un exemple : .PP .Vb 2 \& package Another_Class; \& @ISA = qw(Some_Class); \& \& my %ClassData = ( \& CData1 => "", \& ); \& \& sub CData1 { \& my($self, $newvalue) = @_; \& if (@_ > 1) { \& # d'abord, enregistrer localement \& $ClassData{CData1} = $newvalue; \& \& # ensuite, passer le relais a\*` la premie\*`re \& # version surcharge\*'e, si elle existe \& if ($self\->can("SUPER::CData1")) { \& $self\->SUPER::CData1($newvalue); \& } \& } \& return $ClassData{CData1}; \& } .Ve .PP Ces remarques sur l'he\*'ritage multiple conservent leur inte\*'re\*^t au\-dela\*` de la premie\*`re surcharge. .PP .Vb 6 \& for my $parent (@ISA) { \& my $methname = $parent . "::CData1"; \& if ($self\->can($methname)) { \& $self\->$methname($newvalue); \& } \& } .Ve .PP Pour ame\*'liorer les performances de manie\*`re significative, il est possible d'utiliser directement la me\*'thode &UNIVERSAL::can qui renvoie une re\*'fe\*'rence directe a\*` la fonction : .PP .Vb 5 \& for my $parent (@ISA) { \& if (my $coderef = $self\->can($parent . "::CData1")) { \& $self\->$coderef($newvalue); \& } \& } .Ve .PP Si vous surde\*'finnissez \f(CW\*(C`UNIVERSAL::can\*(C'\fR dans votre propre classe, assurez-vous de retourner correctement cette re\*'fe\*'rence. .Sh "Fermer la porte et jeter la cle\*'" .IX Subsection "Fermer la porte et jeter la cle'" Avec l'imple\*'mentation actuelle, tout code se trouvant dans la porte\*'e lexicale du fichier contenant \f(CW%ClassData\fR a la possibilite\*' de modifier directement ce dernier. Est-ce correct ? Peut-on accepter d'autoriser, ou si vous pre\*'fe\*'rez, peut-on souhaiter autoriser d'autres parties de l'imple\*'mentation de cette classe a\*` acce\*'der directement aux attributs de classe ? .PP Cela de\*'pend de ce que vous entendez par \*(L"e\*^tre soigneux\*(R". Pensez a\*` la classe \&\f(CW\*(C`Cosmos\*(C'\fR. Si la me\*'thode \f(CW&supernova\fR avait pu directement modifier \&\f(CW$Cosmos::Stars\fR ou \f(CW$Cosmos::Cosmos{stars}\fR, alors nous n'aurions pas e\*'te\*' capables de re\*'utiliser la classe pour cre\*'er \f(CW\*(C`Multiverse\*(C'\fR. Ceci montre de\*'finitivement que ce n'est certainement pas une bonne ide\*'e que d'autoriser l'acce\*`s aux attributs de classe par la classe elle\-me\*^me par un autre moyen que les me\*'thodes d'acce\*`s. .PP On ne peut pas ge\*'ne\*'ralement pas imposer a\*` la classe un acce\*`s restreint a\*` ses attributs de classe, me\*^me dans les langages fortement oriente\*'s\-objet. Mais c'est possible en Perl. .PP En voici une manie\*`re : .PP .Vb 1 \& package Some_Class; \& \& { # ouvrir une porte\*'e lexicale pour cacher $CData1 \& my $CData1; \& sub CData1 { \& shift; # XXX: inutilise\*' \& $CData1 = shift if @_; \& return $CData1; \& } \& } \& \& { # ouvrir une porte\*'e lexicale pour cacher $CData2 \& my $CData2; \& sub CData2 { \& shift; # XXX: inutilise\*' \& $CData2 = shift if @_; \& return $CData2; \& } \& } .Ve .PP Personne \- absolument personne \- n'est autorise\*' a\*` lire ou modifier les attributs de classe autrement qu'au moyen de la me\*'thode d'acce\*`s, puisque seule cette me\*'thode a acce\*`s a\*` la variable lexicale qu'elle ge\*`re. Cette utilisation d'une me\*'thode d'acce\*`s aux attributs de classe est une forme de proprie\*'te\*' largement plus puissante que celles fournies par la plupart des langage \s-1OO\s0. .PP La duplication du code utilise\*'e pour les me\*'thodes d'acce\*`s aux donne\*'es irrite notre paresse, nous allons donc utiliser des fermetures pour cre\*'er des me\*'thodes similaires. .PP .Vb 1 \& package Some_Class; \& \& { # ouvrir une porte\*'e lexicale pour les me\*'ta\-objets ultra\-prive\*'s des attributs de classe \& my %ClassData = ( \& CData1 => "", \& CData2 => "", \& ); \& \& for my $datum (keys %ClassData ) { \& no strict "refs"; \& *$datum = sub { \& use strict "refs"; \& my ($self, $newvalue) = @_; \& $ClassData{$datum} = $newvalue if @_ > 1; \& return $ClassData{$datum}; \& } \& } \& } .Ve .PP La fermeture ci-dessus peut e\*^tre modifie\*'e afin d'he\*'riter des me\*'thodes \&\f(CW&UNIVERSAL::can\fR et \f(CW\*(C`SUPER\*(C'\fR ainsi qu'on l'a vu pre\*'ce\*'demment. .Sh "Retour sur la transparence" .IX Subsection "Retour sur la transparence" La classe \f(CW\*(C`Vermin\*(C'\fR montre la transparence au moyen d'une variable de paquetage, nomme\*'e e\*'ponymement \f(CW%Vermin\fR, comme son me\*'ta\-objet. Il est impossible d'utiliser cette strate\*'gie si l'on pre\*'fe\*`re ne pas utiliser de variables de paquetage hormis celles ne\*'cessaires a\*` l'he\*'ritage ou e\*'ventuellement au module \f(CW\*(C`Exporter\*(C'\fR. Ce n'est pas tre\*`s satisfaisant, parce que les attributs transparents repre\*'sentent une technique attirante, aussi vaut-il mieux envisager une imple\*'mentation qui utilise uniquement les variables lexicales. .PP Une deuxie\*`me raison pourrait conduire a\*` e\*'viter les hash de paquetage e\*'ponymes. A trop utiliser des noms de classe contenant des caracte\*`res \*(L"deux\-points\*(R" double\*'s, on finit par obtenir quelque chose que l'on n'a pas voulu. .PP .Vb 4 \& package Vermin; \& $class = "Vermin"; \& $class\->{PopCount}++; \& # accesses $Vermin::Vermin{PopCount} \& \& package Vermin::Noxious; \& $class = "Vermin::Noxious"; \& $class\->{PopCount}++; \& # accesses $Vermin::Noxious{PopCount} .Ve .PP Dans le premier cas, le nom de classe n'a pas de \*(L"double\-deux\-points\*(R", on obtient le hash du paquetage courant. Mais dans le deuxie\*`me cas, au lieu de cela, on obtient le hash \f(CW%Noxious\fR du paquetage Vermin. (La vermine nocive ache\*`ve tout juste d'envahir un autre paquetage et de semer ses donne\*'es autour d'elle :\-) Perl ne connai\*^t pas la notion de paquetage relatif dans ses conventions de nommage, aussi les double-deux-points impliquent une inspection pleinement qualifie\*'e pluto\*^t que confine\*'e au paquetage courant. .PP Dans la pratique, il est peu probable que la classe \f(CW\*(C`Vermin\*(C'\fR posse\*`de une variable de paquetage nomme\*'e \f(CW%Noxious\fR que vous auriez vous\-me\*^me cre\*'e\*'. Si vous e\*^tes tre\*`s me\*'fiant, vous pourriez toujours parier sur votre territoire dont vous connaissez les re\*`gles, par exemple en utilisant a\*` la place les noms de classe \f(CW\*(C`Eponymous::Vermin::Noxious\*(C'\fR ou \f(CW\*(C`Hieronymus::Vermin::Boschious\*(C'\fR ou encore \f(CW\*(C`Leave_Me_Alone::Vermin::Noxious\*(C'\fR. Il est certain que l'existence d'une autre classe nomme\*'e \f(CW\*(C`Eponymous::Vermin\*(C'\fR posse\*'dant son propre hash \&\f(CW%Noxious\fR est the\*'oriquement possible, et cela sera toujours vrai. Personne n'arbitre les noms de paquetage. On trouvera toujours un cas ou\*` une variable globale telle que \f(CW@Cwd::ISA\fR explosera si plus d'une classe se sert du me\*^me paquetage \f(CW\*(C`Cwd\*(C'\fR. .PP S'il vous reste encore un relent de paranoi\*:a, il existe une autre solution. Rien n'oblige a\*` utiliser une variable de paquetage pour un me\*'ta\-objet de classe, ni pour une classe univalentes, ni pour les attributs transparents. Il suffit simplement de coder les me\*'thodes afin qu'elles aient un acce\*`s lexical. .PP Voici une autre imple\*'mentation de la classe Vermin avec la se\*'mantique utilise\*'e pre\*'ce\*'demment, mais n'utilisant pas de variables de paquetage. .PP .Vb 1 \& package Vermin; \& \& # Voici le me\*'ta\-objet de classe nomme\*' e\*'ponymement. \& # Il contient toutes les donne\*'es de classe, ainsi que toutes les donne\*'es d'intance \& # on peut donc utiliser ce qui suit pour les deux initialisations \& # ainsi que la transparence. C'est un mode\*`le. \& my %ClassData = ( \& PopCount => 0, # majuscules pour les attributs de classe \& color => "beige", # minuscules pour les attributs d'instance \& ); \& \& # me\*'thode "constructeur" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub spawn { \& my $obclass = shift; \& my $class = ref($obclass) || $obclass; \& my $self = {}; \& bless($self, $class); \& $ClassData{PopCount}++; \& # initialiser les champs a\*` partir de l'objet appelant, ou les \& # omettre si l'objet appelant est la classe, afin de fournir la transparence \& %$self = %$obclass if ref $obclass; \& return $self; \& } \& \& # me\*'thode d'acce\*`s transparente pour l'attribut "color" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub color { \& my $self = shift; \& \& # prend en charge l'invocation sur une classe \& unless (ref $self) { \& $ClassData{color} = shift if @_; \& return $ClassData{color} \& } \& \& # prend en charge l'invocation sur un objet \& $self\->{color} = shift if @_; \& if (defined $self\->{color}) { # cela n'existe pas ! \& return $self\->{color}; \& } else { \& return $ClassData{color}; \& } \& } \& \& # me\*'thode d'acce\*`s d'attribut de classe pour l'attribut "PopCount" \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub population { \& return $ClassData{PopCount}; \& } \& \& # destructeur d'instance ; invoque\*'e uniquement en tant que me\*'thode d'objet \& sub DESTROY { \& $ClassData{PopCount}\-\-; \& } \& \& # determine la transparence d'un attribut d'objet \& # (typiquement ?) invoque\*'e uniquement en tant que me\*'thode d'objet \& sub is_translucent { \& my($self, $attr) = @_; \& $self = \e%ClassData if !ref $self; \& return !defined $self\->{$attr}; \& } \& \& # teste la pre\*'sence d'un attribute dans la classe \& # invoque\*'e en tant que me\*'thode de classe ou me\*'thode d'objet \& sub has_attribute { \& my($self, $attr) = @_; \& return exists $ClassData{$attr}; \& } .Ve .SH "NOTES" .IX Header "NOTES" L'he\*'ritage est un me\*'canisme tre\*`s puissant mais tre\*`s subtil, que l'on utilisera mieux apre\*`s mu\*^re re\*'flexion. En revanche, l'agre\*'gation repre\*'sente souvent une meilleure approche. .PP Il n'est pas possible d'utiliser des variables dans la porte\*'e lexicale du fichier avec les modules \f(CW\*(C`SelfLoader\*(C'\fR ou \f(CW\*(C`AutoLoader\*(C'\fR, parce qu'ils modifient la porte\*'e lexicale dans laquelle s'expriment les me\*'thodes du module lors de la compilation. .PP Il faudrait sans aucun doute prendre en compte les modifications de paquetage peu claires avant de cre\*'er les noms des attributs d'objet. Par exemple, \&\f(CW\*(C`$self\->{ObData1}\*(C'\fR devrait pluto\*^t s'appeler \f(CW\*(C`$self\->{ _\|_PACKAGE_\|_ . "_ObData1" }\*(C'\fR, mais cela aurait rendu les exemples plus confus. .SH "VOIR AUSSI" .IX Header "VOIR AUSSI" perltoot, perlobj, perlmod, et perlbot. .PP Il n'est pas ininte\*'ressant de jeter un coup d'oeil aux modules \&\f(CW\*(C`Tie::SecureHash\*(C'\fR et \f(CW\*(C`Class::Data::Inheritable\*(C'\fR que l'on peut trouver sur \s-1CPAN\s0. .SH "AUTEUR ET COPYRIGHT" .IX Header "AUTEUR ET COPYRIGHT" Copyright (c) 1999 Tom Christiansen. All rights reserved. .PP This documentation is free; you can redistribute it and/or modify it under the same terms as Perl itself. .PP Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit would be courteous but is not required. .SH "REMERCIEMENTS" .IX Header "REMERCIEMENTS" Russ Allbery, Jon Orwant, Randy Ray, Larry Rosler, Nat Torkington, and Stephen Warren all contributed suggestions and corrections to this piece. Thanks especially to Damian Conway for his ideas and feedback, and without whose indirect prodding I might never have taken the time to show others how much Perl has to offer in the way of objects once you start thinking outside the tiny little box that today's \*(L"popular\*(R" object-oriented languages enforce. .SH "HISTORIQUE" .IX Header "HISTORIQUE" Dernie\*`re e\*'dition : Sun Feb 4 20:50:28 \s-1EST\s0 2001 .SH "TRADUCTION" .IX Header "TRADUCTION" .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" Traduction initiale : Jean-Pierre Vidal . Mise a\*` jour: Paul Gaborit . .Sh "Relecture" .IX Subsection "Relecture" Aucune pour le moment.