.\" 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.