Perl est sans nul doute un langage puissant. Mais lorsqu'il s'agit de parler "Objet", cette puissance vire très vite au cryptique, voire même au pur mystique.
L'objectif de ce tutorial est simple de proposer un guide de démarrage permettant de mettre oeuvre le plus rapidement possible, les concepts objets en Perl.
A l'évidence, l'approche adoptée par les concepteurs de Perl lors de la mise en oeuvre de cet aspect a plus été comment fabriquer des objets avec du Perl ?
que comment modifier Perl pour lui ajouter une syntaxe Object ?
.
Le résultat en est une forte impression d'objet fait main
. Alors certes, on dispose ainsi d'un très (trop) grand contrôle sur des aspects qui son généralement laissés au compilateur (héritage, surcharge, etc.). Mais en contrepartie Perl ne vous guide absolument pas avec sa syntaxe "pâte à modeler" et cela implique une certaine difficulté à implémenter ses premiers objets dans ce langage, particulièrement lorsque l'on vient d'un monde Java ou même C++.
Les packages Perl (package) permettaient déjà de créer des espaces de noms pouvant contenir des fonctions, des variables, exportées vers l'extérieur ou pas.
Petit exemple classique, un hello world. Voyons tout d'abord le module qui contient notre package, notre "classe statique" :
Maintenant le script qui va utiliser notre package :
Enfin pour lancer l'ensemble :
Cet espace de nom HelloWorld peut ainsi être vu comme un objet ne contenant que des méthodes et des champs (my, our) statiques. L'appel à ces éléments statiques se faisant par la syntaxe ::.
Pour permettre de transformer un paquet en classe, les concepteurs de Perl utilisent la notation ->. Pour comprendre la différence entre :: et ->, rédigeons un petit exemple :

Lançons maintenant notre test :
Le premier appel à TestAppel::afficheArgument indique très logiquement qu'il n'y a pas d'arguments. En revanche, surprise dans le second appel, TestAppel->afficheArgument indique qu'il y a, contre toute attente, un argument, qui se trouve être le nom du package.
Ce que cela indique, c'est que la notation PACKAGE->méthode insère un premier argument, une chaîne de caractère; qui est le nom du paquet qui contient la fonction. En d'autres termes, la classe de notre futur objet. Et c'est cette propriété qui va nous permettre de fabriquer le constructeur de notre objet.
Nous allons donc rajouter une nouvelle méthode à HelloWorld.pm nommée new. Cette méthode va être le constructeur de notre classe HelloWorld.
La première étape est expliquée au chapitre précédent, on utilise la syntaxe -> pour récupérer dans $class le nom du paquet, donc de la classe de notre futur objet.
Deuxième étape, création d'une instance anonyme d'objet... En réalité il s'agit là d'une pauvre table de hash vide. En effet, pour Perl, un objet c'est une table de hash qui contient toutes les informations spécifiques à l'instance (les camps ou attributs).
Étape 3, la partie mystique, on va bénir la variable $self avec le nom de la classe. C'est cette étape va concrètement créer notre objet.
Dernière étape enfin, la fonction new retourne la nouvelle instance de notre classe. Il ne nous reste plus qu'à l'utiliser en réécrivant notre script d'appel :
Nous voyons ici que nous avons bien, en retour de la fonction new, un objet avec le bon nom de classe représenté par une table de Hash. Notre premier objet. Champagne !
Qui dit construction, dit destruction. Normalement Perl utilise un concept de "ramasse-miettes" proche de celui présent en python, java ou .Net. L'idée est que le système trouve tout seul les objets non utilisés, et les détruit lui-même. Mais même si cette destruction est automatique, il peut être important que notre objet soit prévenu de cette destruction, par exemple pour libérer des ressources (descripteur de fichier, thread, etc.).
Comme en perl un objet n'est autre chose qu'un paquet, les mêmes mécanismes sont utilisables, dont la fonction DESTROY. La différence est que c'est ici une fonction d'instance qui est invoquée, et que le paramètre $self est donc disponible. Le destructeur aura donc la forme suivante :
new est notre première méthode, mais une méthode un peu spécial, un constructeur. Ajoutons maintenant une méthode plus classique à HelloWorld.pm :
la foncton Perl ref(), renvoie pour une instance donnée, la nom de classe associé. Ajoutons maintenant,juste après le constructeur un appel à la méthode créée, dans helloWorld.pl:
Et relançons notre script :
Nous constatons ici que le fait d'utiliser la syntaxe -> sur l'instance de l'objet créée par new insère elle aussi un argument à la méthode, mais cette fois il s'agit de l'instance de notre objet. C'est grâce à cela que notre méthode va savoir sur quel objet elle travaille.
Alors un objet avec des méthodes sans attributs, ça manque quelque peu de sel... Le premier réflexe serait d'utiliser des my pour des champs privés et des our pour les publiques. Mais rien n'est moins faux car, comme nous l'avons vu au premier chapitre, ces variables appartiennent au paquet, ce sont donc des attributs statiques (accessibles par HelloWorld::).
Pour créer des champs manipulables par instance, il faut jouer avec notre table de Hash, celle-là même que nous avons bénit dans le constructeur. Nous allons donc modifier le module HelloWorld.pm pour déclarer deux champs. M'un "privé" (comprendre ici non-documenté), messageBienvenue, et l'autre "publique", destinataire. Nous allons aussi modifier notre fonction direBonjour pour en faire une méthode de l'objet et utiliser ces nouveaux attributs.
Et maintenant modifions notre script :
Alors le point nouveau ici est l'introduction de cette étrange syntaxe $self->{attribut} qui permet de stocker des associations nom/valeur dans la table de hash de l'instance, et de relire ces valeurs. A noter que toutes ces valeurs sont considérées comme des champs publiques. Le reste n'est pas bien compliqué.
Maintenant, en programmation objet, donner ainsi l'accès aux champs sans contrôle n'est jamais une bonne idée. C'est pour cela que l'on a inventé les accesseurs, des méthodes permettant de lire et d'écrire les valeurs dans les champs, mais après contrôle. Les accesseurs permettent aussi de restreindre l'accès en lecture, en écriture, ou cacher purement le champ (s'il n'y a pas d'accesseur).
Une méthode (personnelle) pour réaliser en Perl de tels accesseurs peut être la suivante :
Maintenant modifions le fichier helloWorld2.pl pour utiliser notre accesseur :
Du coup, nous disposons du champ $self->{destinataire}, utilisé en interne dans l'objet, relativement protégé de l'extérieur. Et la méthode
Nous avons une classe, un constructeur, des instances, des méthodes statiques et d'instance, des champs statiques et d'instances, des accesseurs, bref, nous voilà presque complet. Il nous manque juste la notion d'héritage.
Faire hériter une classe d'une autre classe, se fait très simplement par l'utilisation de la syntaxe use base et une bidouille dans le constructeur. Pour tester cela, nous allons créer un nouveau module HelloUniverse.pm :
La seule partie un peu nouvelle est, mise à part l'utilisation naturelle de use base, le point 1 qui consiste à récupérer une instance de la classe de base à travers l'exécution constructeur. Comme il s'agit d'une instance déjà bénite, on peut utiliser la méthode héritée destinataire (point 2) pour définir un nom par défaut. Ensuite on re-bénit (point 3) l'instance avec la classe de l'objet courant HelloUniverse. Et c'est tout. Du coup, notre helloUniverse.pl va ressembler à cela :
Il suffit de lancer ce script pour voir un convaincant Hello Universe apparaître.
Déjà, que l'on soit pour ou contre l'héritage multiple (plusieurs classes de base), ce dernier est disponible en Perl. Prenons l'exemple de l'ajout de constantes à notre classe HelloUniverse. Les constantes peuvent passer par un héritage à la classe Exporter qui permet de faire remonter des contantes dans l'espace de noms qui utilise le module.
Nous allons donc ajouter, après le use base "HelloWorld" le code suivante :
Pour utiliser notre constante dans helloUniverse.pl ajoutons avant le new HelloUniverse :
En relançant l'execution, nous voyons apparaître la valeur de PI.
Maintenant surchargeons le script direBonjour de sorte à modifier le comportement apporté par la classe HelloWorld :
Toute la magie est dans la dernière ligne et la syntaxe $self->SUPER:: qui permet de faire appel à la méthode de la classe de base.
Pour conclure, voyons comment utiliser tout cela pour fabriquer un objet singleton, c'est à dire un objet dont il ne peut y avoir qu'une seule instance. D'abord un code d'exemple :
Et pour tester notre singleton :
A l'exécution, vous constatez que les timestamp sont bien les mêmes, il s'agit donc du même objet à chaque appel, que ce soit par un :: ou un ->.
La seule chose un peu nouvelle là dedans est l'utilisation d'un champ statique (my $instance) et privé pour stocker notre instance bénite si elle ne l'est pas déjà (!defined). Ensuite, vu que l'on veut appeler la méthode Instance par les deux syntaxes (:: ou ->), on ne peut compter sur un classique my $class=shift; (voir les premiers chapitres). On utilise donc à la place la macro Perl __PACKAGE__ qui est remplacée à la compilation par le nom complet du paquet.
Voilà, fin du petit tour sur les objets en Perl. Alors il faut être bien conscient qu'il doit y avoir autant de méthode d'implémentation d'objet en Perl que de développeur, c'est même une des grandes caractéristiques de ce langage. Celle-ci est la mienne et elle est bien sur critiquable, et tout critique est évidemment la bienvenue
.
- répondre
yoho , le 7 December, 2007 - 15:23Bravo et merci !
- répondre
Ulhume, le 7 December, 2007 - 16:08De rien
ET même si Perl semble bien attaqué par Python, je continue à le préférer, pour ce qui touche au script. Car au fond, le python c'est du perl sans les accolades 
- répondre
Dab, le 7 December, 2007 - 19:57Comme toujours excellent
Pout faciliter la vie en Perl Objet il existe aussi le très récent (mi 2006) module 'Moose'. Peut être connais tu ?
sinon jette un oeil à cette initiation : http://www.sukria.net/fr/archives/2007/08/20/jouons-avec-moose-ou-commen...
- répondre
Ulhume, le 8 December, 2007 - 08:32Yep j'avais vu moose lorsque je faisais des recherches pour écrire l'article. Mais je dois être bizarre, car je trouve cette syntaxe encore moins naturelle que celle d'origine...
- répondre
sukria , le 14 December, 2007 - 14:20Cette syntaxe que tu ne trouve pas naturelle, est inspirée de celle de Perl 6.
Moose/Coat est incontestablement une excellente meta-classe pour Perl 5. Elle permet en plus de faire des choses très séduisante, comme les hooks par exemple.
- répondre
Ulhume, le 14 December, 2007 - 17:57@sukria Je ne dis pas le contraire, je ne suis pas non plus un spécialiste de Perl, ni ne vise à le devenir un jour.
Tu sais, sans me chercher d'église, je suis plutôt un "vieux" du monde Java et j'ai du coup une vision du développement très "typé" et très orienté objet. En conséquence, la manière, très pragmatique j'en conviens, d'adapter l'objet à Perl (plus que l'inverse), me laissera du coup toujours sur ma faim et me semblera, par conséquence, toujours relativement peu "naturelle". Il me manquera toujours avec perl une ensemble de notions comme les visibilités, les interfaces ou une abstraction forte pour ne parler que des plus importantes... et ceci sans parler d'une une syntaxe elle aussi... objet.
En d'autre termes un
use Moose;
has 'name' => (
is => 'ro',
required => 1,
isa => 'Str'
);
me semblera toujours moins naturel qu'un
public property int name read name;
public Heros(String name) {
this.name=name;
}
}
Ceci est donc une question de culture et de besoin. Et Lisp, même si je sais l'utiliser, ne m'est non plus pas très "naturel"
Poster un nouveau commentaire