Cours gratuits » Cours informatique » Cours programmation » Cours Perl » Guide de formation perl pdf avec exemples et exercices

Guide de formation perl pdf avec exemples et exercices

Problème à signaler:

Télécharger



★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Perl et la programmation orientée objet

A. Brève introduction de Perl et de la Programmation Orientée Objet

La Programmation Orientée Objet (qu'on notera POO) est un concept possédant de nombreuses vertus universellement reconnues à ce jour. C'est une méthode de programmation qui permet d'améliorer le développement et la maintenance d'applications, de logiciels avec un gain de temps non négligeable. Il est important de garder à l'esprit qu'elle ne renie pas la programmation structurée (ou procédurale) puisqu'elle est fondée sur elle. L'approche classique pour introduire la POO dans un langage existant est de passer par une encapsulation des fonctionnalités existantes. Le langage C++ s'est construit sur le C et a apporté la POO. Le langage JAVA est fondé sur la syntaxe du C++.

Perl a également suivi le pas en proposant des extensions pour donner la possibilité à ses fans

 de pouvoir utiliser ce paradigme de programmation. Néanmoins, Perl étant permissif, il n'est pas aussi strict que des langages "pur objet". Mais c'est normal, car la philosophie de Perl est maintenue, "there is more than one way to do it" (TIMTOWTDI).

Dans les sections A et B nous allons vous expliquer comment fonctionne la POO en Perl traditionnellement, néanmoins en Perl moderne, certains modules facilitent grandement et rendent bien plus élégante la POO, nous vous les présenterons en section C.

A-1. Avantages

Il est plus facile et rapide de modifier, de faire évoluer, de maintenir du code issu d'un logiciel, d'une application de moyenne ou de grande envergure avec la POO. L'architecture du code peut permettre à d'autres applications de réutiliser des composants, des classes

D'autres développeurs peuvent facilement utiliser vos programmes. C'est le cas des modules du CPAN que l'on a l'habitude d'utiliser : ils sont tous écrits en POO ! Je tiens à tout de même préciser que les modules simples sont aussi réutilisables. La clarté d'un module n'est pas liée au paradigme de programmation.

A-2. Inconvénients

La philosophie de Perl peut être un inconvénient à la POO car il existe des dizaines de façons différentes d'écrire un programme, des modules

Sachez que la POO dégrade en général les performances et qu'il est important de savoir si ce paradigme de programmation est adapté à votre problématique. La POO nécessite sans doute une plus grande réflexion quant à la définition de l'architecture du programme, avant de passer à la programmation (ce qui peut être un avantage). Dans tous les cas, comme le disait Damian Conway :

Utilisez la POO pour ses avantages et malgré ses inconvénients et pas simplement parce que c'est le gros marteau familier et confortable qui figure dans votre boite à outils.

B. Les bases de la POO en Perl

B-1. Les classes et les objets

Lorsque l'on débute en POO, les notions de classe et d'objet peuvent nous paraître abstraites. Une mauvaise compréhension peut être fatale pour la suite de cet article. Voici une explication de Sylvain Lhuiller que je trouve simple et concise.

        ?    Explication de la POO en Perl par Sylvain Lhuiller

La programmation orientée objet est un type de programmation qui se concentre principalement sur les données. La question qui se pose en POO est « quelles sont les données du problème ? » par opposition à la programmation procédurale par exemple, qui pose la question « quelles sont les fonctions/actions à faire ? ». En POO, on parle ainsi d'objets, auxquels on peut affecter des variables/attributs (propriétés) et des fonctions/actions (méthodes).

On parle de « classe », qui est une manière de représenter des données et comporte des traitements : une classe « Chaussure » décrit, par exemple, les caractéristiques d'une chaussure. Elle contient un champ décrivant la pointure, la couleur, la matière, etc. Une telle classe comporte de plus des traitements sur ces données ; ces traitements sont appelés « méthodes ». Grossièrement une méthode est une fonction appliquée à un objet. Exemples de méthode : monter, coudre, ressemeler

Une fois définie une telle classe, il est possible d'en construire des instances : une instance de classe est dite être un objet de cette classe. Dans notre exemple, il s'agirait d'une chaussure dont la pointure, la couleur et la matière sont renseignées.

B-1-a. Définition d'une classe

Commençons les choses sérieuses en parlant Perl! Vous verrez qu'il est simple de parler Perl avec l'accent orienté objet  !

Définir une classe est très simple car ce n'est rien d'autre qu'un package, un module en Perl. Un objet n'est autre chose qu'une référence à un scalaire, un hachage, un tableau (voire une fonction, un typeglob ) qui est liée à cette classe.

Voici notre classe "Personne" :

packagePersonne;    # Nom du package, de notre classe usewarnings;        # Avertissement des messages d'erreurs usestrict;          # Vérification des déclarations useCarp;            # Utile pour émettre certains avertissements

#

#

1;       # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes après elle

Voilà! notre classe est créée, simple non !! Il suffit de créer un package du nom de la classe dans un fichier .pm du même nom.

Notre script principal appelle la classe via un use habituel.

usePersonne;

B-1-b. Définition d'un constructeur

Pour pouvoir créer un objet et instancier la classe "Personne", on doit définir un constructeur dans la classe, c'est-à-dire le fichier (sachez qu'un script ".pm" peut avoir plusieurs classes, mais dans cet article, on travaillera avec une classe par fichier    ".pm"). 

Créer un constructeur Perl revient à créer une fonction spéciale nommée "new". Le constructeur peut être nommé "create", "forge" ou tout autre nom. Et il peut exister plusieurs constructeurs. Perl n'impose pas grand-chose sur le constructeur comme dans les autres langages OO. Néanmoins, c'est le nom standard utilisé en Perl POO. Dans un souci de maintenance et de clarté, pourquoi déroger à la règle !! Sachez seulement que ce n'est pas un opérateur comme dans certains langages POO.

 

Notre classe "Personne" est caractérisée par son nom, son prénom, son âge, son sexe et son nombre d'enfants. Ces informations sont transmises par l'utilisateur de notre classe (le script principal) au constructeur. Ce dernier s'attend donc à 5 arguments. Mais en fait non ! Ce sera 6 car le premier argument correspond toujours au nom de la classe. Mais l'utilisateur de la classe n'a pas à se soucier de cela, il ne passe que 5 arguments au constructeur. Créons 5 champs (attributs), attributs pour cette classe (nom, prénom, âge, sexe, nombre d'enfants).

- constructeur

 

subnew{

  my( $classe, $nom, $prenom, $age, $sexe, $nombre_enfant ) = @_;

  #

}

 
 

Tout constructeur doit déterminer si son premier argument est le nom de la classe ou une référence :

- constructeur : vérification de la classe

$classe = ref($classe) || $classe;

Si $classe est une référence (ce qui signifie que c'est une instance d'une classe, on en parlera plus loin dans l'article), on prend ref($classe) (c'est-à-dire que le constructeur a été appelé à partir d'un objet $djibril->new();), sinon, $classe est la chaîne contenant la classe (c'est-à-dire que le constructeur a été appelé à partir du nom de la classe : Personne>new();). ref retourne le nom de la classe lorsque la référence passée en paramètre est une référence à un objet (et non une référence simple, non blessed).

Vous pouvez d'ores et déjà tester pour vous amuser.


- constructeur : vérification de la classe

 

$classe = ref($classe) || $classe; print $classe;

 

Script principal

 

#!/usr/bin/perl warnings; usestrict; useCarp;

usePersonne;

 my$Personne = Personne->new();

 
 
 
 
 

Cela             a            pour            effet            d'afficher             la            chaîne Personne.

Créons maintenant une référence anonyme à un hachage vide qui stockera les attributs de notre classe et deviendra l'objet de la classe. Nous la nommons $this.

 

Nous avons choisi de nommer notre variable « $this » par analogie aux autres langages OO, mais ce n'est pas obligatoire. D'ailleurs, si vous consultez le code source de certains modules

Perl      du      CPAN,      vous      verrez      qu'elles      sont      souvent      nommées $self.

 

vous obtiendrez ceci Personne=HASH(0x235bb0) alors qu'avant la bénédiction (bless), on obtenait HASH(0x235bb0). L'adresse mémoire correspondant au hachage est liée à la classe.

Vous                ne                dormez                pas                encore                    j'espère  !!

Stockons maintenant les attributs de notre classe que l'utilisateur passera via le constructeur.

- constructeur : arguments

$this->{_NOM} = $nom;

$this->{_PRENOM} = $prenom;

$this->{AGE}           = $age; $this->{_SEXE}         = $sexe; $this->{NOMBRE_ENFANT} = $nombre_enfant;

Voilà, fini !! Plus qu'à retourner l'objet à l'utilisateur !

 

en majuscules. De plus, certaines clés sont précédées d'un souligné "_", cela signifie

que les valeurs des clés ne devraient pas être modifiables par l'utilisateur. Ce ne sont que des conventions, mais je vous les recommande.

Dans notre exemple, une personne ne peut pas changer de nom, de prénom et de sexe !! 

Voilà à quoi ressemble notre premier constructeur !

- constructeur

 

subnew{

  my( $classe, $nom, $prenom, $age, $sexe, $nombre_enfant ) = @_; 

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Création de la référence anonyme de hachage vide (futur objet)   my$this = {};

  # Liaison de l'objet à la classe   bless $this, $classe;

$this->{_NOM}          = $nom;

  $this->{_PRENOM} = $prenom;

$this->{AGE}           = $age;

$this->{_SEXE}         = $sexe;

$this->{NOMBRE_ENFANT} = $nombre_enfant;

  return$this;

}

 
 

B-1-c. Les objets

Avec ce peu de code, on peut déjà instancier notre classe en créant des objets depuis notre script principal.

Script principal - Objet

 

#!/usr/bin/perl usewarnings; usestrict; useCarp;

 usePersonne;

 my$Objet_Personne1 = Personne->new('Dupont', 'Jean', 45, 'M', 3);

 
 
 
 
 

Nous venons de créer notre objet, on pourrait en créer plusieurs. Remarquez bien la notation : la classe est suivie d'une flèche qui pointe sur le constructeur "new" auquel on passe des arguments. Il existe une autre notation correcte, mais pouvant porter à confusion.

 

méthode Personne à laquelle on passe des arguments. Ce qui est bien sûr faux ! Donc gardez en tête la notation recommandée avec flèche.

B-1-d. Amélioration du constructeur et de la création de l'objet

Avant de continuer notre apprentissage de Perl et la POO, prenons de bonnes habitudes. Notre constructeur a un petit inconvénient : Il attend 5 arguments. Vous me direz oui et alors  !!! Il est dangereux en termes de maintenance et de lisibilité d'écrire le code comme énoncé ci-dessus. En effet, on ne sait jamais dans quel ordre passer les arguments, il est facile de se tromper. De plus, si l'on souhaite ne pas préciser l'âge de notre personne, il faudrait penser à passer un undef. La bonne manière consiste donc à passer une référence anonyme à un hachage avec en clé des variables claires et compréhensibles et en valeur la donnée. Voici ce que l'on peut obtenir :

- constructeur

 

subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Création de la référence anonyme d'un hachage vide (futur objet)   my$this = {};

  # Liaison de l'objet à la classe   bless( $this, $classe );

$this->{_NOM}          = $ref_arguments->{nom};

$this->{_PRENOM}       = $ref_arguments->{prenom};

$this->{AGE}           = $ref_arguments->{age};

$this->{_SEXE}         = $ref_arguments->{sexe};

$this->{NOMBRE_ENFANT} = $ref_arguments->{nombre_enfant};

$this;

}

 
 

Script principal

 

my$Objet_Personne1 = Personne->new(

  { nom           => 'Dupont', prenom        => 'Jean',

 
 

age           => 45,     sexe          => 'M',     nombre_enfant => 3,

  }

);

C'est quand même beaucoup plus propre et évolutif non ?

B-1-e. Remarques sur les constructeurs, objets et attributs

•    Il est possible d'avoir plusieurs constructeurs dans une classe. Ces derniers seront conçus de la même manière que le constructeur new. Il faudra cependant penser à changer de nom !

•    Un objet peut être un autre type de référence que celle d'un hachage.

•    L'utilisateur peut voir le contenu de l'objet (puisque c'est un hachage), il suffit d'utiliser le module Data::Dumper.

Script principal

 

useData::Dumper;

print Dumper $Objet_Personne1;

 
 

Résultat - On obtient

 

$VAR1 = bless(

  { '_PRENOM'       => 'Jean',

    'AGE'           => 45,

    '_SEXE'         => 'M',

'NOMBRE_ENFANT' => 3,     '_NOM'          => 'Dupont'

  },

  'Personne'

);

 

•    L'utilisateur peut donc modifier le contenu

Il peut modifier les valeurs des attributs de la classe puisqu'ils ne sont pas protégés ou en créer de nouveaux.

Script principal

 

useData::Dumper;

my$Objet_Personne1 = Personne->new(

{ nom           => 'Dupont',     prenom        => 'Jean', age           => 45,     sexe          => 'M',     nombre_enfant => 3,

  } );

print Dumper $Objet_Personne1;

# modification de l'objet

$Objet_Personne1->{_NOM}    = 'toto'; $Objet_Personne1->{MECHANT} = 'developpeur'; print "===== modification =====\n"; print Dumper $Objet_Personne1;

 
 
 

Résultat

 

$VAR1 = bless( {

'_PRENOM' => 'Jean',

'AGE' => 45,

'_SEXE' => 'M',

'NOMBRE_ENFANT' => 3,

'_NOM' => 'Dupont'

}, 'Personne' );

===== modification =====

$VAR1 = bless( {

'MECHANT' => 'developpeur',

'_PRENOM' => 'Jean',

'AGE' => 45,

'_SEXE' => 'M',

'NOMBRE_ENFANT' => 3,

'_NOM' => 'toto'

}, 'Personne' );

Perl fait confiance à ses bons développeurs. Donc ces derniers n'iront pas jouer avec les attributs de la classe !

Lorsque vous utilisez une voiture, vous ne vous préoccupez pas de savoir comment elle a été conçue, de connaître le rôle d'une vis lambda du moteur. Vous n'utilisez que ce qui vous a été mentionné dans la notice. De toute façon, pourquoi prendre le risque de modifier le comportement d'une classe qui fonctionne bien ? Vous pouvez toujours vous amuser à modifier les pièces du moteur de votre voiture dans votre jardin au risque de ne plus pouvoir rouler avec !!

Sinon, il est généralement de bonne pratique de créer une méthode permettant d'obtenir la valeur d'un attribut ou de la modifier. On les appelle des accesseurs ou mutateurs.

B-2. Les méthodes

Une méthode n'est autre chose qu'une fonction en Perl. La seule différence est qu'elle a

toujours en premier argument l'objet créé par le constructeur. On parle de méthode d'instance. Si cet argument est plutôt le nom de la classe ($classe), on parle de méthode de classe.

B-2-a. Les méthodes d'instance

Une méthode d'instance est une fonction Perl qui s'applique à une instance de la classe (à l'objet). Elle a toujours pour premier argument l'objet ($this). Pour illustrer cela, créons deux méthodes dans la classe "Personne". On donne la possibilité à une personne de pouvoir marcher et parler, logique !!

- méthode marcher et parler

 

# Méthode marcher - ne prend aucun argument submarcher {   $this = shift;

print "[$this->{_NOM} $this->{_PRENOM}] marche\n";

  return;

}

 
 

# Méthode parler - un argument subparler {

  my( $this, $message ) = @_;

 

contre, pour parler, il faut dire quelque chose même si ce n'est pas obligatoire ! Pour appeler ces méthodes, c'est simple :

 

Supposons que l'on souhaite modifier la valeur du champ age. Le mauvais programmeur va modifier directement le contenu de l'objet en faisant ceci :

Script principal

 

#!/usr/bin/perl warnings; usestrict; useCarp;

 usePersonne;

my$Objet_Personne1 = Personne->new( {   nom               => 'Dupont',     prenom            => 'Jean',    age               => 45,        

  sexe                 => 'M', 

  nombre_enfant => 3,

}); 

print $Objet_Personne1->{AGE},"\n"; $Objet_Personne1->{AGE} = 22; print $Objet_Personne1->{AGE},"\n";

 
 
 
 
 
 
 

 

perléens codent proprement, ils liront la documentation de la classe et trouveront les méthodes adéquates que voici :

 

d'une personne. La méthode modifier_age attend un argument $age afin de modifier l'âge de notre personne. Certains développeurs préfèrent avoir une unique méthode du même nom que l'attribut jouant le rôle de mutateur et d'accesseur comme ici :

 

sinon elle le modifie. Il est vrai que cette méthode est simple, qu'il y a moins de codes à maintenir, mais il est tout de même préférable d'avoir un mutateur et accesseur par attribut pour ces quelques raisons :

•    au premier regard, c'est beaucoup plus lisible ;

•    il y a peu de chances de confondre une méthode quelconque avec une méthode ayant le rôle de mutateur/accesseur ;

•    il est inutile de faire des tests à chaque appel d'un accesseur, on retourne juste la

valeur.


De plus, si l'on fait appel à un mutateur $objet->age($age); avec $age = undef;, Perl ne signalera aucune erreur car la méthode age se comportera comme un accesseur. Il est alors très difficile de trouver d'où vient l'erreur dans un tel programme. Pourtant, confondre obtenir_age et modifier_age n'est pas dramatique, car il est plus facile de retrouver l'erreur. Voilà !!

Script principal

print "Mon age : ",$Objet_Personne1->obtenir_age(),"\n";

$Objet_Personne1->modifier_age(22); print "Mon nouvel age : ",$Objet_Personne1->obtenir_age(),"\n";

résultat

45

Mon nouvel age : 22

C'est quand même beaucoup plus propre ! Vous pouvez maintenant créer deux méthodes par attribut si vous jugez nécessaire que l'utilisateur puisse le lire et le modifier. 

Petit résumé, voici à quoi ressemble notre classe "Personne" actuellement :

 

packagePersonne;    # Nom du package, de notre classe usewarnings;        # Avertissement des messages d'erreurs usestrict;          # Vérification des déclarations

useCarp;    # Utile pour émettre certains avertissements 

# Constructeur de la classe Personne subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Création de la référence anonyme à un hachage vide (futur objet)   my$this = {};

# Liaison de l'objet à la classe   bless( $this, $classe );

$this->{_NOM}          = $ref_arguments->{nom};

$this->{_PRENOM}       = $ref_arguments->{prenom};

$this->{AGE}           = $ref_arguments->{age};

$this->{_SEXE}         = $ref_arguments->{sexe};

$this->{NOMBRE_ENFANT} = $ref_arguments->{nombre_enfant};

 return$this;

}

# accesseur obtenir_nom subobtenir_nom {   $this = shift;   return$this->{_NOM};

}

 
 
 
 
 
 
 

 

  }

  # Le premier caractère de la phrase est mis en majuscule

  $message = ucfirst($message);

  print "[$this->{_NOM} $this->{_PRENOM}] $message\n";

  return1;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

B-2-b. Les méthodes de classes ou statiques

Une méthode de classe s'applique à toute une classe et non à l'instance de la classe (l'objet $this). Une variable statique est une variable commune à toute une classe, exemple :

 

tout moment dans la classe. Mais comme il se trouve qu'elles ont aussi une portée globale à tout le package, elles sont accessibles partout et à tout moment dans le module. $VariablePublique déclarée avec our est publique car elle peut être accessible en dehors de la classe, du module. $NbrPersonnes déclarée avec my est privée, sa portée se limite à la classe "Personne", au module.

Script principal

 

#!/usr/bin/perl usewarnings; usestrict; useCarp;

usePersonne;

print "NbrPersonnes ",$Personne::NbrPersonnes,"\n"; print "VariablePublique ",$Personne::VariablePublique,"\n";

 
 
 
 
 

Résultat

 

 

la classe "Personne", il faut écrire$Personne::NbrPersonnes ($NomDeLaClasse::NomVariable). Dans la classe "Personne", on a déclaré $VariablePubliqueavec our, elle est donc publique. Par contre, $NbrPersonnes est privée (à cause de my) et sa portée s'arrête à la classe. C'est pour cette raison que dans notre exemple ci-dessus, on n'arrive pas à afficher le contenu de $NbrPersonnes.

Une méthode de classe n'a accès qu'aux variables statiques et non à l'objet $this. Elle a la particularité de prendre en premier argument le nom de la classe (comme le constructeur).

Pour mieux comprendre, créons une méthode nommée "Obtenir_nbr_personnes" dont le but sera de nous retourner le nombre de personnes créées dans notre script. Dans le constructeur new, on incrémentera la variable statique $NbrPersonnes.

- méthode statique Obtenir_nbr_personnes

 

# Comptage du nombre de personnes créées

my$NbrPersonnes = 0;        # champ statique privé

# Constructeur de la classe Personne subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Création de la référence anonyme à un hachage vide (futur objet)   my$this = {};

  # Liaison de l'objet à la classe   bless( $this, $classe );

$this->{_NOM}          = $ref_arguments->{nom};

$this->{_PRENOM}       = $ref_arguments->{prenom};

$this->{AGE}           = $ref_arguments->{age};

$this->{_SEXE}         = $ref_arguments->{sexe};

  $this->{NOMBRE_ENFANT} = $ref_arguments->{nombre_enfant}; 

  # Nombre de personnes créées.

  $NbrPersonnes++;

  return$this;

}

# méthode de classe Obtenir_nbr_personnes subObtenir_nbr_personnes {   my$classe = shift;

$NbrPersonnes;

}

 
 
 
 

Pour appeler cette méthode dans notre script principal, la notation sera légèrement différente.

Script principal

 

#!/usr/bin/perl warnings; usestrict; useCarp;

usePersonne;

 my$Objet_Personne1 = Personne->new(

  { nom           => 'Dupont', prenom        => 'Jean',     age           => 45,     sexe => 'M',     nombre_enfant => 3,

  }

); 

print "Nbr personne : ",Personne->Obtenir_nbr_personnes(),"\n";

 my$Objet_Personne2 = Personne->new(

{ nom           => 'Durand',     prenom        => 'Djibril', age           => 35,     sexe          => 'M',     nombre_enfant => 0,

  } ); print "Nbr personne : ",Personne->Obtenir_nbr_personnes(),"\n";

 
 
 
 
 
 
 

Résultat

 

Nbr personne : 1

Nbr personne : 2

 

La flèche est placée sur le nom de la classe et non sur l'objet. N.B. En écrivant $Personne1->Obtenir_nbr_personnes();, on aurait eu le même résultat car dans notre méthode, on n'utilise pas l'argument $classe, mais si c'était le cas, nous aurions eu quelques soucis.

B-3. Bon à savoir : la protection des méthodes

Il est possible de protéger les méthodes d'une classe en utilisant des références. En créant une référence à une fonction (méthode) ayant une portée limitée au package via my, la méthode devient inaccessible de l'extérieur de la classe.

méthodes privées

 

my$_private_method =sub{ $this = shift;

   my@argument = @_;

  #

 
 

Sachez qu'il existe différentes façons de créer des méthodes à la volée en utilisant les "fermetures" en Perl, mais cela sort du cadre de cet article.

B-4. Le destructeur

Lorsqu'un objet est détruit, Perl libère la mémoire occupée par lui. Un objet $Personne1 est

détruit lorsqu'il est égal à undef ou à la fin de sa portée (ex : s'il a été créé dans un bloc

(for,       if,      else ),       il       sera       détruit       à       la       fin      de       la      boucle).

Le destructeur est une méthode spéciale nommée "DESTROY" qui est appelée (si elle est définie dans la classe), lorsqu'un objet est détruit, juste avant la libération de la mémoire

de celui-ci. Contrairement au constructeur dont le nom est laissé libre au programmeur, celui du destructeur est forcé par les mécanismes OO de Perl.

Elle peut nous permettre d'effectuer des tâches avant la destruction de l'objet :

•    fermer une connexion réseau, se déconnecter d'une base de données, fermer un fichier ;

•    afficher un message ;

•    appeler le destructeur d'une classe héritée ; ? décrémenter le nombre de personnes créées ; ? etc.

Voici notre destructeur :

- destructeur

 

# Destructeur subDESTROY {   my$this = shift;

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n"; 

  # Diminuer le nombre de personnes créées.

  $NbrPersonnes--;

;

}

 
 

Comme les méthodes d'instance, son premier argument doit être l'objet $this et c'est tout.

Perl           se           charge           de           l'appeler           et           du           reste           !

__PACKAGE__ est une variable de paquetage Perl qui contient le nom de la classe. 

 

l'objet, et affiche Bye bye. Nous remarquons que DESTROY est appelée au bon moment.

B-5. L'encapsulation

L'encapsulation est un mécanisme consistant à rassembler les données, les attributs et les méthodes au sein d'une classe en cachant l'implémentation de l'objet. C'est un concept permettant de protéger l'intégrité des données d'un objet et de donner la possibilité à l'utilisateur de lire ou modifier les valeurs des attributs via des méthodes accesseurs, mutateurs.

Pour être plus précis, l'utilisateur ne doit utiliser que ce qu'on lui permet. S'il n'y a pas de mutateurs, il ne doit pas pouvoir modifier les attributs. Il ne doit pouvoir utiliser que les méthodes publiques. Le concepteur d'une classe se doit de protéger le reste.

Lorsque vous utilisez une voiture, vous ne vous préoccupez pas de savoir comment elle a

été conçue, de connaître de rôle d'une vise lambda du moteur. Vous n'utilisez que ce qui vous a été mentionné dans la notice. Cela n'a pas empêché les constructeurs des voitures de protéger l'accès à certaines parties du moteur afin d'éviter que des malins fassent n'importe quoi. Je sais, c'est un peu tiré par les cheveux, mais faut bien trouver un exemple parlant.

Certains langages de programmation comme JAVA, C++, C# utilisent des mots-clés comme public, private, protected pour protéger des méthodes, des classes. Mais comme vous le savez, Perl est permissif. Il fait confiance à ses programmeurs de bonne éducation  ! Une des conventions à respecter est que toute méthode ou nom d'attribut précédé d'un souligné "_" est privé (Ex : sub _methode {}).

Si vous ne faites pas trop confiance à vos confrères programmeurs, il est possible de créer des attributs et méthodes vraiment privés. Cette notion de protection sera abordée dans la de cet article.

B-6. L'héritage

Le concept d'héritage constitue l'un des fondements de la POO. Il permet la réutilisation des classes, donc d'hériter des fonctionnalités (attributs, méthodes). Perl permet de bénéficier de ce concept.

B-6-a. L'héritage simple

Pour l'illustrer, créons deux nouvelles classes "Homme" et "Femme". Un homme ou une femme est une personne, donc a un nom, un prénom, un âge, un sexe, et un nombre d'enfants. Un homme peut être barbu ou non. Ces classes héritent de la classe "Personne", donc de ses attributs et de ses méthodes.

•    Lien de filiation

Signalons le lien de filiation entre classes au moyen du module base.

 

d'autres documentations, il est mentionné d'utiliser le tableau @ISA.

 

établies par des affectations à la phase d'exécution. En utilisant le module base, on définit la hiérarchie d'une classe avec une déclaration à la phase de compilation. On s'assure ainsi que l'héritage est établi dès que possible et de charger automatiquement le module .

•    Le constructeur

La conception d'un constructeur d'une classe héritant d'une autre est légèrement différente d'une classe n'héritant d'aucune. Rappelez-vous, lorsque nous avons créé notre constructeur new dans la classe "Personne", nous avons créé une référence anonyme à un hachage vide. Maintenant, dans la classe "Homme", ce ne sera pas le cas. Notre classe "Homme" utilisera l'objet de la classe "Personne".

 

(Personne). La variable $this du constructeur de la classe "Homme" (ou "Femme") est ainsi l'équivalent du $this de la classe "Personne" et on lui passe les attributs qu'il attend. ? Les attributs

Prenons pour exemple la classe "Homme". Son constructeur réceptionne les arguments que le script principal lui transmet (nom, prenom, sexe, age, nombre_enfant et le nouveau barbu). Ensuite, il ne se préoccupe pas de créer l'attribut _NOM,_PRENOM, AGE, _SEXE et NOMBRE_ENFANT puisque la classe "Personne" dont il hérite s'en charge. Pour ce faire, il suffit de transmettre ces arguments à la classe "Personne" via la pseudoclasse SUPER citée ci-dessus. Nous récupérons ainsi l'objet de la classe "Personne" $this. Nous la bénissons afin de la lier à notre classe "Homme" (avec bless), puis nous y ajoutons l'attribut barbu (le petit nouveau). Maintenant notre objet est prêt à être renvoyé au script principal. Voici le résultat ci-dessous :

- constructeur

 

# Constructeur de la classe Homme subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Objet de notre classe héritée   my$this = $classe->SUPER::new(

    { nom           => $ref_arguments->{nom},       prenom        => $ref_arguments->{prenom},       age           => $ref_arguments->{age},       sexe          => 'M',

      nombre_enfant => $ref_arguments->{nombre_enfant},

    }

  );

  # Liaison de l'objet à la classe   bless( $this, $classe );

  # Nouvel attribut

  $this->{BARBU} = $ref_arguments->{barbu};

  return$this;

}

 
 

Le constructeur de la classe "Femme" est presque identique à deux lignes près. Le sexe sera 'F' et il n'y aura pas d'attribut barbu, logique !!

Créons l'accesseur et le mutateur de l'attribut barbu.

- accesseur/mutateur barbu

# accesseur obtenir_barbu

 

d'enfants, etc. Les classes "Homme" et "Femme" héritent déjà de ces méthodes. C'est beau l'héritage !!

•    Le destructeur

Dans le cas de l'héritage, il faut penser à appeler également le destructeur de la classe mère (Personne), car Perl s'arrête au premier destructeur rencontré. C'est-à dire qu'à la mort de notre objet $Objet_Personne1 dans le script principal, Perl appelle le destructeur de la classe "Homme" (ou "Femme") puis libère la mémoire. Il n'est pas encore assez intelligent pour penser à appeler le destructeur de la classe dont il hérite ("Personne"), faut tout lui dire !!. Pour ce faire, on recourt à la pseudoclasse SUPER.

ou - Destructeur

 

# Destructeur subDESTROY {   my$this = shift;

  # destructeur de la classe mère.

print "=>Appel du destructeur de la classe mère\n\n"; $this->SUPER::DESTROY();

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n";

;

}

 
 

Faisons un petit test et admirez le résultat.

Script principal

 

#!/usr/bin/perl usewarnings; usestrict; useCarp;

 useHomme;

 
 
 
 

my$Objet_Personne1 = Homme->new(

  { nom           => 'Dupont',     prenom        => 'Jean',     age           => 45,     nombre_enfant => 3,

  }

);

print "Bonjour\n"; $Objet_Personne1 = undef; print "Bye bye";

 
 

Résultat

 

Bonjour

=>Appel du destructeur de la classe mère

===================================================

L'objet de la classe Personne va mourir

===================================================

===================================================

L'objet de la classe Homme va mourir

===================================================

Bye bye

 

•    Les méthodes

Comme il a été dit ci-dessus, nos classes "Homme" et "Femme" héritent de la classe "Personne". De ce fait, elles héritent de ses méthodes. Si dans notre script principal, nous souhaitons créer une femme (objet femme) et que nous souhaitons la faire parler, il suffit juste de faire appel à la méthode parler comme nous savons déjà le faire :

Script principal

 

useFemme;

my$Femme = Femme->new(

{ nom           => 'Dupont',     prenom        => 'Anne', age           => 45,     nombre_enfant => 3,

  }

);

$Femme->parler("bonjour");

 
 
 

Résultat

 

[Dupont Anne] Bonjour

 

Il n'y a rien d'autre à faire. Notre classe "Femme" n'a pas besoin d'avoir une méthode parler. En fait, Perl cherche la méthode parler dans la classe "Femme", il ne la trouve pas et continue sa recherche dans la classe héritée (Personne). Il la trouve et l'exécute. C'est magique !

•    Redéfinition de méthodes

Supposons maintenant que l'on souhaite faire parler dans notre script principal un homme, le principe est le même que pour une femme, on appelle la méthode parler de la même façon. Pour une raison quelconque, nous décidons qu'un homme doit s'affirmer quand il parle, et la seule façon de le faire est de préciser qu'il est un homme à chaque fois qu'il parle

(effet de la testostérone            !). Lorsque le script principal lui demande de dire "bonjour", au lieu d'afficher "[Dupont Jean] Bonjour" par exemple comme c'était le cas, il affichera "[Dupont           Jean]    Bonjour            (je suis      un        homme)" !!!

Pour faire cela, on ne va pas créer une nouvelle méthode s'appelant par exemple parler_homme, pourquoi ? Imaginons qu'on ait une centaine de scripts utilisant notre classe "Homme", ça veut dire que pour rendre nos hommes virils, il faudrait ouvrir tous les scripts et changer les méthodes parler en parler_homme en faisant attention de ne pas en oublier ou de se tromper en faisant parler une femme comme un homme. En terme de maintenance, c'est une catastrophe. Utilisons tout simplement le concept de redéfinition.

Créons une méthode parler dans notre classe "Homme". On dira qu'on a redéfini la méthode parler car cette dernière existe déjà dans la classe mère "Personne". Voici à quoi elle ressemble :

 

on rajoute la phrase "(je suis un homme)" sinon, notre message est "je suis un homme et Ne sais pas quoi dire". Ensuite, au lieu faire un print, on fait appel à la méthode parler de la classe mère "Personne" (autant profiter de l'héritage et ne pas réinventer la roue).

 

useHomme;

my$Homme1 = Homme->new(

  { nom           => 'Dupont',     prenom        => 'Jean',     age           => 45,     nombre_enfant => 3,

  }

);

$Homme1->marcher();

$Homme1->parler('Bonjour');

 
 
 
 

[Dupont Jean] Marche

 

[Dupont Jean] Bonjour (je suis un homme)

Créons maintenant une méthode accouche dans notre classe "Femme" qui hérite évidemment de la classe "Personne", elle ne redéfinit aucune méthode. Elle permet à une femme d'accoucher d'un nombre x d'enfant(s) qu'on lui passe en argument.

 

signaler son accouchement d'un nombre d'enfants. Ensuite elle met à jour le champ NOMBRE_ENFANT en utilisant le mutateur de la classe "Personne". Maintenant vous maîtrisez le mécanisme d'héritage simple. Passons à l'héritage multiple !

B-6-b. L'héritage multiple

Une classe peut hériter des fonctionnalités de plusieurs classes, c'est l'héritage multiple. Pour continuer sur notre lancée, nos hommes et femmes ont besoin de travailler pour subvenir à leurs besoins quotidiens. Par conséquent ils vont payer des impôts pour le bien de notre pays. Nous devons créer une méthode qui sera capable de calculer leurs impôts. Pour ce faire, nous avons besoin de connaître le montant du salaire et pourquoi pas le métier tant qu'à faire. Créons donc une classe nommée "Activite"dont les attributs seront SALAIRE et METIER. Cette classe aura des mutateurs et accesseurs pour les attributs metier et salaire, puis une méthode impot_a_payer, sans oublier notre constructeur. Voici la classe :

 

packageActivite; # Nom du package, de notre classe usewarnings; # Avertissement des messages d'erreurs usestrict; # Vérification des déclarations

useCarp; # Utile pour émettre certains avertissements 

# Constructeur de la classe Personne subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Objet de notre classe héritée   my$this = {};

  # Liaison de l'objet à la classe   bless( $this, $classe );

  $this->{METIER}  = $ref_arguments->{metier};   $this->{SALAIRE} = $ref_arguments->{salaire};

  return$this;

}

# accesseur obtenir_salaire subobtenir_salaire {   my$this = shift;

  return$this->{SALAIRE};

}

# mutateur modifier_salaire submodifier_salaire {

  my( $this, $salaire ) = @_;

  if( defined $salaire ) {

    $this->{SALAIRE} = $salaire;

  } 

  return;

}

 
 
 
 
 
 
 
 

 

C'est une fonction exportée du module Carp. C'est l'équivalent d'un die. Sauf qu'au lieu de donner le numéro de la ligne du module ".pm" quand elle sera appelée, elle donnera le

numéro de la ligne du script où la méthode impot_a_payer a été appelée. C'est plus facile pour         l'utilisateur de        savoir   d'où      vient     le         problème.

Bon, c'est bien, mais on ne parle toujours pas d'héritage multiple !!! Nous y voilà. Pour que nos classes "Homme" et "Femme"puissent utiliser les méthodes de la classe "Activité", héritons-en. Rien de plus simple, une seule ligne est à changer dans les classes "Homme" et "Femme".

et

 

usebaseqw ( Personne Activite ); # hérite de la classe Personne et

Activite

 
 

 

méthode impot_a_payer. Notre classe "Femme" hérite de la classe "Personne" et "Activite". L'ordre est important. Lorsque nous appelons la méthode impot_a_payer dans le script principal, Perl va chercher s'il existe dans la classe "Femme", puis dans la classe "Personne", puis continue sa recherche dans les classes dont "Personne" hérite (aucune), puis dans "Activite", etc.

Si nous avions une méthode impot_a_payer dans la classe Personne, c'est elle qui aurait été appelée. 


Remarque : : Supposons que vous êtes dans une classe "Enfant" qui hérite des classes "Personne" et "JeuxVideos".

 

deux classes héritées. Si vous faites :

$this->SUPER::parler('blablabla');

Perl cherche dans l'ordre et la trouve en premier dans la classe "Personne". Pour contourner ce problème, vous devez l'appeler ainsi :

 

•    L'ordre d'héritage multiple a une importance, faites attention.

•    Dans vos classes, soyez sûr des méthodes que vous appelez dans le cas d'héritage multiple.

•    Choisissez des noms d'attributs compréhensibles.

•    Faites attention à ne pas écraser vos noms d'attributs (clés de hachage) en cas d'héritage.

•    Dans vos classes héritant de plusieurs autres, n'oubliez pas d'appeler les destructeurs des classes mères (voirClass::ISA).

N'hésitez pas à tester les valeurs de vos attributs. Vous imaginez bien qu'un homme ne peut pas         avoir -2 enfant(s).

Lors d'un héritage multiple, comme dans notre classe "Homme" et "Femme", SUPER ne désignant que la première classe de base (classe "Personne"), seul le constructeur de cette base peut être appelé, et par conséquent, les méthodes des autres classes sont bien "héritées", mais les propriétés doivent être initialisées grâce aux mutateurs de ces autres classes de base. Pour revenir à nos classes "Homme" et "Femme", elles héritent des classes "Personne" et "Activité". Leurs constructeurs via la pseudoclasse SUPER utilisent le constructeur de la classe "Personne". Si nous souhaitons donner le nom d'un métier à notre homme ou notre femme, il faut utiliser le mutateur modifier_metier (héritée de la classe "Activite").

B-7. Le polymorphisme

Le terme de polymorphisme indique qu'une entité peut apparaître suivant plusieurs formes. C'est un concept puissant en POO qui complète l'héritage. Il permet en quelque sorte le choix d'une méthode parmi plusieurs de même nom quand la situation se présente. En fait, nous l'avons déjà abordé sans le savoir.

Si vous vous souvenez, dans nos classes "Personne" et "Homme", nous avons une méthode parler. Dans notre script principal, en fonction que l'on utilise la classe "Homme" ou "Femme", la méthode parler utilisée n'est pas la même car nous avons redéfini cette méthode dans notre classe "Homme", on peut parler de polymorphisme. Cette possibilité de redéfinir une méthode dans des classes héritant d'une classe de base s'appelle en POO pure "la spécialisation".

Le polymorphisme en Perl se fait tout seul grâce au module base ou à la variable @ISA. On

peut aussi soit redéfinir une méthode, ou bien choisir d'appeler la méthode de la classe héritée que l'on souhaite.

Rappel sur la recherche de méthodes : lorsque vous appelez une méthode sur un objet, l'interpréteur Perl la cherche premièrement dans la classe de l'objet. Puis il monte dans la hiérarchie de classes jusqu'à la trouver. Si la recherche est infructueuse dans l'arbre d'héritage, il revient à la classe la plus dérivée et recommence.

B-8. Modules et méthodes utiles

B-8-a. Version d'une classe

Il est important de donner une version à votre classe. Cela permet de suivre facilement l'évolution de votre classe :

 

En Perl, il existe une classe mère, ancêtre de toutes les classes, c'est la classe UNIVERSAL. Elle contient trois méthodes isa, can et VERSION.

Quelle est l'utilité de ces méthodes ?

•    isa

Si nous souhaitons connaître la classe principale d'un objet, on peut utiliser la fonction Perl ref. Pour rappel, lorsque vous appelez cette fonction sur une référence, elle vous retourne le type de la structure de données sur laquelle elle pointe (SCALAR, ARRAY, HASH ). De ce fait, ref($objet); retournera le nom de la classe.

 

qui est le cas via l'héritage), ref ne peut pas nous aider. Il faut recourir à la méthode isa de la classe UNIVERSAL.

print "\$Femme1 appartient objet de la classe Femme\n"    if(

$Femme1->isa('Femme') );

print "\$Femme1 appartient objet de la classe Activite\n"if(

$Femme1->isa('Activite') );

print "\$Femme1 appartient objet de la classe Personne\n"if(

$Femme1->isa('Personne') ); print "\$Femme1 appartient objet de la classe Homme\n"    if(

$Femme1->isa('Homme') );

$Femme1 appartient objet de la classe Femme

$Femme1 appartient objet de la classe Activite

$Femme1 appartient objet de la classe Personne

On constate qu'isa nous indique que notre objet $Femme1 n'appartient pas à la classe "Homme", encore heureux, mais qu'il appartient bien aux autres.

•    can

La méthode can renvoie la référence d'une méthode appelée si elle existe, sinon, elle nous retourne undef.

print $Femme1->can('parler'); # CODE(0x187df20) print $Femme1->can('toto'); # use of uninitilized value in print

•    VERSION

La méthode VERSION renvoie la version de notre classe si cette dernière a été mentionnée. Elle permet :

•    d'obtenir la version de notre classe. Si elle n'en a pas, un message d'erreur est retourné ;

•    de vérifier que la version de la classe est plus grande qu'un réel qu'on lui passerait en argument.

Supposons que notre programme principal utilise la classe "Femme". Nous souhaitons vérifier que la version de la classe "Femme" est récente et au moins supérieure à la version 4.3 dans le cas contraire, notre script ne fonctionnera pas. On va appeler la méthode VERSION et lui passer en paramètre 4.3.

 


Mauvaise version

Femme version 4.3 required--this is only version 1.07 at

Version : 1.0

B-8-c. Méthode AUTOLOAD

La méthode AUTOLOAD est appelée automatiquement par l'interpréteur Perl lorsqu'il ne trouve pas une méthode d'une classe. Lorsque vous appelez une méthode sur un objet, l'interpréteur Perl la cherche premièrement dans la classe de l'objet. Puis il monte dans la hiérarchie de classes jusqu'à la trouver. Si la recherche est infructueuse dans l'arbre d'héritage, il revient à la classe la plus dérivée et recommence. Dans cette seconde recherche, il cherche la méthode AUTOLOAD

Cette méthode ne favorise pas l'efficacité, la concision, la robustesse et la maintenabilité. Il faut l'éviter complètement pour plusieurs raisons que je ne citerai pas ici.

En voici une en provenance du livre Perl Best Practices de Damian Conway :

L'AUTOLOAD, "le-plus-à-gauche-en-recherche-en-profondeur" dont l'objet hérite sera appelé systématiquement pour traiter tous les appels à une méthode inconnue. C'est un problème. Si la hiérarchie de la classe de l'objet comporte deux définitions AUTOLOAD() ou plus, il se peut que la seconde soit plus à même de traiter tel ou tel appel de méthode inconnue. Mais normalement, ce deuxième sous-programme n'aura jamais la possibilité de le faire.

B-8-d. Module Class::ISA

Ce module permet de lister toutes les classes mères d'une classe. Il retourne la hiérarchie des classes dérivées. Il fournit trois méthodes qui ne sont pas exportables. Pour les appeler, il faut écrire :

1.    Class::ISA::super_path($Classe) : retourne un tableau contenant les classes mères dans l'ordre de recherche, $Classe n'est pas inclus dans le tableau ;

2.    Class::ISA::self_and_super_path($Classe) : retourne un tableau contenant les classes mères dans l'ordre de recherche, $Classe est inclus dans le tableau et se trouve en première position ;

3.    Class::ISA::self_and_super_versions($Classe) : retourne un hachage contenant en clés les classes mères et en valeurs, les versions.

Ces méthodes peuvent être utiles pour l'appel des destructeurs des classes mères dans l'héritage simple et multiple.

B-8-e. Autres modules

Il existe plusieurs modules sur le CPAN qui permettent de faire de la POO en Perl différemment. Ils peuvent nous fournir des mécanismes pour mieux protéger nos attributs, méthodes, etc. Ces modules sont pour la plupart nommés Class::*, Object::*, nous parlerons de certains de ces modules plus tard dans cet article.

Module Carp

•    Utilisez la méthode croak de ce module au lieu d'utiliser die

•    Utilisez la méthode carp au lieu d'utiliser la fonction warn

useCarp;

croak('A la place de die'); carp('A la place de warn');

croak() et carp() servent à se plaindre d'un mauvais usage de votre classe et signale donc l'erreur dans le script appelant, s'il s'agit d'un dysfonctionnement interne, il faut utiliser die() et warn().

Dans notre constructeur, nous vérifions que l'utilisateur passe en argument les attributs nom, prenom et sexe. S'il en oublie, on stoppe le script (car cela sera fatal pour notre script) en lui précisant la ligne d'erreur dans son script avec croak.

 

 

B-9. Modèles, classes complètes et scripts

Modèle                 de                 nos                 classes                 et                 méthodes

Cette image a été créée à partir du module UML::Class::Simple qui permet de créer une image représentant toutes les classes d'un module, d'un répertoire, etc. Pour l'utiliser, il est nécessaire d'installer. L'image nous montre que les classes "Homme"et "Femme" héritent des classes "Personne" et "Activite". Pour chaque classe, les méthodes publiques sont affichées.

Code complet de toutes nos classes et script principal les utilisant

Le but du script est de faire parler six personnes afin de jouer avec toutes les méthodes que nous avons créées. Vous pouvez les télécharger.

Classe

 

packagePersonne;    # Nom du package, de notre classe usewarnings;        # Avertissement des messages d'erreurs usestrict;          # Vérification des déclarations

useCarp;            # Utile pour émettre certains avertissements

usevars qw($VERSION);    # Version de notre module

$VERSION = '1.00';

# Comptage du nombre de personnes créées

my$NbrPersonnes = 0;     # champ statique privé

# Constructeur de la classe Personne subnew{

  my( $classe, $ref_arguments ) = @_; 

  # Vérifions la classe

  $classe = ref($classe) || $classe; 

  # Création de la référence anonyme à un hachage vide (futur objet)   my$this = {};

  # Liaison de l'objet à la classe   bless( $this, $classe );

  # Vérification des champs nom, prenom et sexe   foreachmy$attribut (qw/ nom prenom sexe /) {     unless( defined $ref_arguments->{$attribut} ) {       croak("[Attention] Attribut $attribut manquant\n");

 
 
 
 
 
 
 
 

    }

 }


unless( $ref_arguments->{sexe} =~ m{^M|F$}i ) {

 

return$this->{NOMBRE_ENFANT};

}

 

return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

 


$this->{BARBU} = $barbu;

  } 

  return$this->{BARBU};

}

# Méthode parler (un argument) qui va redéfinir la méthode parler

# de la classe mère subparler {

  my( $this, $message ) = @_;

  unless( defined $message ) {

    $message = 'je suis un homme et Ne sais pas quoi dire';   }

  $this->SUPER::parler( $message . ' (je suis un homme)' );

  return1;

}

# Destructeur subDESTROY {   $this = shift;

  # destructeur de la classe mère.   print "=>Appel du destructeur de la classe mère\n\n"; $this->SUPER::DESTROY();

  # Avertissement que l'objet va mourir

  print "===================================================\n"; print "L'objet de la classe " . __PACKAGE__ . " va mourir\n";   print "===================================================\n";

  return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

 
 
 

Classe

 

packageFemme;    # Nom du package, de notre classe usewarnings;     # Avertissement des messages d'erreurs usestrict;       # Vérification des déclarations

useCarp;         # Utile pour émettre certains avertissements

usebaseqw ( Personne Activite );   # hérite de la classe Personne et Activite

usevars qw($VERSION);               # Version de notre module $VERSION = '1.00';

# Constructeur de la classe Personne subnew{

( $classe, $ref_arguments ) = @_; 

  # Vérifions la classe

  $classe = ref($classe) || $classe;

  # Objet de notre classe héritée   my$this = $classe->SUPER::new(

   { nom           => $ref_arguments->{nom},

 
 
 
 
 
 
 
 

prenom        => $ref_arguments->{prenom},

age           => $ref_arguments->{age},       sexe          => 'F',

      nombre_enfant => $ref_arguments->{nombre_enfant},

    }

  );

# Liaison de l'objet à la classe   bless( $this, $classe );

  return$this;

}

# méthode accouche subaccouche {

  my( $this, $nombre_enfant ) = @_;

  if( defined $nombre_enfant ) {     unless( $nombre_enfant =~ m{^\d+$} ) {       croak "Mauvais argument : $nombre_enfant\n";       return;

    }

$this->parler("accouche de $nombre_enfant enfant(s)");

    my$NbrEnfant = $this->obtenir_nombre_enfant() + $nombre_enfant;

$this->modifier_nombre_enfant($NbrEnfant);

  } 

  return;

}

# Destructeur subDESTROY {   my$this = shift;

  # destructeur de la classe mère.   print "=>Appel du destructeur de la classe mère\n\n"; $this->SUPER::DESTROY();

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n";

  return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

 
 
 

Classe

 

packageActivite;    # Nom du package, de notre classe usewarnings;        # Avertissement des messages d'erreurs usestrict;          # Vérification des déclarations

useCarp;            # Utile pour émettre certains avertissements

 usevars qw($VERSION);    # Version de notre module

$VERSION = '1.00';

# Constructeur de la classe Personne

 
 
 
 
 
 

subnew{

  my( $classe, $ref_arguments ) = @_;

  # Vérifions la classe

  $classe = ref($classe) || $classe;

 

croak("[Attention] Difficile de calculer vos impôts sans

salaire\n");

}

# Destructeur subDESTROY {   $this = shift;

  # Avertissement que l'objet va mourir

print "===================================================\n"; print "L'objet de la classe " . __PACKAGE__ . " va mourir\n";   print "===================================================\n";

  return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

 
 

Classe

 

#!/usr/bin/perl usewarnings; usestrict; useCarp;

 useFemme; useHomme;

useutf8;    # On a des accents dans le script

# Nous sommes dans une boulangerie print "[A la boulangerie]\n\n";

# 1ere personne = enfant my$enfant = Homme->new(   { nom    => 'Henry', prenom => 'Antoine',     age    => 5,

  }

);

$enfant->parler("bonjour");

# 2eme personne = boulangere my$boulangere = Femme->new(   { nom    => 'Laplanche',     prenom => 'Caroline',     age    => 35,

  }

);

$boulangere->parler("bonjour");

$enfant->parler("comment tu t'appelles ?");

$boulangere->parler(

  " Je m'appelle " . $boulangere->obtenir_prenom() . " et toi ?" ); $enfant->parler( "Je m'appelle "

    . $enfant->obtenir_prenom()

    . $enfant->obtenir_nom() );

$enfant->parler("t'as quel âge ?");

$boulangere->parler( "J'ai " . $boulangere->obtenir_age() . " ans" );

 
 
 
 
 
 
 
 
 

$enfant->parler("pain mame s'il vous plait");

print "\n[Toujours dans la boulangerie, 2 vieux copains "

  . "d'école primaire se rencontrent]\n\n";

 my$copain1 = Homme-new(   { nom    => 'Henry',     prenom => 'Guillaume',     age    => 35,

  }

);

$copain1->parler("comment ça va ?");

 my$copain2 = Homme-new(   { nom    => 'Petit',     prenom => 'Stephane',     barbu  => 1,

  }

);

#

$copain2->parler("bien");

 my$Copain2Barbu = $copain2->obtenir_barbu(); if( defined $Copain2Barbu and $Copain2Barbu == 1 ) {   $copain1->parler("et toi ? tu es barbu maintenant ?");

$copain2->parler("moi ? barbu ? non juste fatigué ");

}

$copain2->parler("ma femme vient d'accoucher, alors les nuits sont courtes");

$copain2->parler("et toi ? tu as combien d'enfants ?");

$copain1->modifier_nombre_enfant(2);

$copain1->parler( "moi j'en ai " . $copain1->obtenir_nombre_enfant()

);

$copain2->parler("quel âge ?");

$copain1->parler( "le dernier a 1 an et commence à marcher. le premier "

    . "commence à parler correctement. d'ailleurs "

    . "c'est lui qui demande du pain à la boulangerie" );

$copain2->parler("tu fais quoi comme métier ?");

$copain1->modifier_metier("ingénieur en informatique");

$copain1->parler( "je suis " . $copain1->obtenir_metier() );

$copain2->parler("Pardon, j'ai pas bien entendu");

$copain1->parler( $copain1->obtenir_metier() );

$enfant->parler("papa, pas de pièce pour le pain");

$copain1->parler("va demander à maman, elle est juste devant la porte");

$enfant->marcher();

 print "\n[Mais maman se prend une prune par la police municipale]

\n\n";

my$FemmeCopain1 = Femme->new(   { nom    => 'Henry',     prenom => 'Anne',

  }

);

$FemmeCopain1->parler( "Quoi ? une contravention pour 5 min de stationnement ? "

    . "Et vous êtes payé combien pour ça ?" );

 my$contractuel = Homme-new(

  { nom    => 'Letellier',     prenom => 'Carlos',     barbu  => 1,

  }

);

$contractuel->parler(

  "1200 par mois, et ce n'est pas encore assez pour me faire insulter

! Vous payez combien d'impôts ?"

);

$FemmeCopain1->modifier_salaire(2000);

$FemmeCopain1->parler(

  "plus de " . $FemmeCopain1->impot_a_payer() . " euros par mois" ); $contractuel->parler(

  "Et bien vous pourrez mettre un visage sur le prochain chèque que vous aurez à régler!"

); ( $FemmeCopain1->obtenir_sexe() eq'M' ) {   $contractuel->parler("Bonne journée monsieur!");

} else{

$contractuel->parler("Bonne journée madame!");

}

$FemmeCopain1->parler( $enfant->obtenir_prenom() . ', '

    . $copain1->obtenir_prenom()

    . " : Allez on rentre à la maison!!" );

print "\n[Conversation terminée]\n";

print "Il y avait ", $boulangere->Obtenir_nbr_personnes(),

  " personne(s) dans la conversation\n\n";

 
 
 
 

C. La modernité de la POO en Perl

A ce stade de l'article, vous maîtrisezles bases de la programmation orientée objet en Perl, les notions de classe, de méthode, de constructeur, la construction d'objets, etc. Nous allons voir des nouveautés qui à ce jour nous facilitent grandement la vie. En fait, certains développeurs de la communauté Perl en avaient assez de devoir à chaque fois réécrire les mêmes codes à savoir, un constructeur, des accesseurs, mutateurs, de l'adapter à Perl 6, etc. Ils trouvaient cela ennuyeux et fastidieux. C'est alors qu'un certain Stevan Little a décidé d'écrire un module qui permet d'écrire moins de code car tout est géré par le module. Il devient encore plus facile de faire de la POO et la lisibilité du code est améliorée. Ce module se nomme Moose. A ce jour, il est recommandé par la communauté Perl pour la POO. Il est puissant, bien pensé et s'appuie sur le modèle objet de Perl 6 (mais ça reste du

Perl                                                                                                                                  5).

Si vous rencontrez des difficultés pour installer ce module, consultez cet, et si ça ne vous aide pas, le forum Perl est là pour vous.

C-1. Moose - la modernité

Nous allons recréer nos classes de façon moderne avec le module Moose qui est stable et déjà bien utilisé en production dans plusieurs entreprises.

Pour vous montrer comment fonctionne ce module, reprenons les exemples utilisés dans la première partie de ce tutoriel. Commençons par créer notre classe "Personne".

 

Créons maintenant les attributs (nom, prenom, age, sexe et nombre_enfant), ainsi que les accesseurs et mutateurs. Le nom, le prénom et le sexe sont non modifiables, par contre, l'utilisateur peut consulter et modifier l'âge et le nombre d'enfants. Le nom, le prénom et le sexe de notre personne doivent obligatoirement être mentionnés. Par la même occasion, on vérifiera que l'âge et le nombre d'enfants sont des entiers, etc. Vous verrez qu'il est possible de faire tout ça en peu de lignes de code.

- attributs

 

# Comptage du nombre de personnes créées

my$NbrPersonnes = 0;     # champ statique privé

# Création des attributs hasnom => (

  is=> 'ro',       # Attribut est lisible et non modifiable   isa      => 'Str',      # Valeur de l'attribut de type chaîne de caractères

  required => 1,          # Attribut obligatoire

  reader => 'obtenir_nom',               # Nom de l'accesseur   trigger =>sub{ $NbrPersonnes++; },   # Incrémente à chaque nouvelle personne

); 

hasprenom => (   is=> 'ro',   isa      => 'Str',   required => 1,

  reader   => 'obtenir_prenom',

 
 
 
 

);  has sexe => (   is=> 'ro',

  isa      => 'Str',   required => 1,

  reader   => 'obtenir_sexe',   trigger =>sub{     my( $this, $sexe ) = @_;

    unless( $sexeeq'M' or $sexeeq'F' ) {

      die("[Attention] Mauvais attribut sexe $sexe : F ou M\n");

    }

  },

);

hasage => (   is=> 'rw',   isa      => 'Int',   required => 0,

  reader   => 'obtenir_age',   writer => 'modifier_age',   trigger  =>sub{     my( $this, $age ) = @_;     unless( $age > 0 ) {

      die("[Attention] Mauvais attribut age : $age\n");

    }

  },

);  hasnombre_enfant => (   is=> 'rw',

  isa      => 'Int',                       # Valeur de type entier   required => 0,

  reader   => 'obtenir_nombre_enfant',

  writer   => 'modifier_nombre_enfant', # Nom du mutateur   default=> 0,                           # Valeur par défaut de l'attribut   trigger =>sub{

    my( $this, $nombre_enfant ) = @_;     unless( $nombre_enfant >= 0 ) {       ("[Attention] Mauvais attribut nombre_enfant :

$nombre_enfant\n");

    }

  },

);

 
 
 

Bon, j'espère qu'au premier coup d'oeil, vous avez compris. Si non, voici quelques explications : 

has est une fonction de Moose permettant de créer les attributs. Son premier argument (avant la flèche =>) est le nom de l'attribut. Après la flèche, ce sont les options. Il existe beaucoup d'options, n'hésitez pas à consulter la documentation

(et). En ce qui concerne la classe "Personne", les attributs nom, prenom et sexe sont en lecture seule. Il sera donc impossible à l'utilisateur de modifier ces trois champs une fois définis dans le constructeur new. Les autres attributs sont rw (read-write), donc Moose nous met à disposition un accesseur et un mutateur. Par défaut, une méthode age (par exemple) est automatiquement créée et joue le rôle d'accesseur et de mutateur, mais comme il est plus judicieux de les séparer, Moose nous le permet. Il suffit de spécifier les options reader et writer. Moose crée ainsi automatiquement les méthodes avec ces noms et la méthode age n'existe plus.

L'option required est aussi très intéressante car elle permet de spécifier les attributs qu'il est obligatoire de passer au constructeur de la classe. Une dernière option que nous avons utilisée est trigger. C'est ce que Moose appelle un déclencheur. Il peuvent s'exécuter avant, pendant ou après l'accès ou la modification des attributs. Dans notre cas, il s'exécute


à                           la                          création                          de                          l'attribut.

Vous pouvez constater que le code est très simple, propre et plus compréhensible.

my($nom, $prenom) =( $this->{nom}, $this->{prenom} );

N.B. Nous n'avons pas besoin de créer un constructeur, Moose le fait pour nous. Voilà, à ce stade, les attributs et notre constructeur sont créés, il ne reste plus qu'à créer nos différentes méthodes.

 

Moose stocke ses informations dans une référence de hachage avec en clé, le nom de l'attribut défini grâce à la fonction has.

En ce qui concerne le destructeur, Moose nous le crée automatiquement. Néanmoins, si nous avons besoin d'un constructeur pour effectuer des tâches précises, il faut utiliser la méthode DEMOLISH fournie par Moose et y mettre notre code. Moose l'exécutera avant la méthode DESTROY.

- Destructeur

 

 

# Destructeur subDEMOLISH {   my$this = shift;

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n"; 

  # Diminuer le nombre de personnes créées.

  $NbrPersonnes--;

   return;

}

 

 
 

Notre          classe "Personne" est          terminée.          Créons         maintenant

les

 

classes "Homme", "Femme" et "Activite". Commençons par la classe "Activite".

 

packageActivite;    # Nom du package, de notre classe

useCarp;            # Utile pour émettre certains avertissements

usevars qw($VERSION);    # Version de notre module

$VERSION = '1.30';

 useMoose;            # Il charge automatiquement strict et warnings

 hassalaire => (   is=> 'rw',   isa => 'Int',   required => 0,

reader   => 'obtenir_salaire',   writer   => 'modifier_salaire',

);

hasmetier => (   is=> 'rw',   isa => 'Str',   required => 0,

  reader   => 'obtenir_metier',   writer => 'modifier_metier',

);

# méthode impot_a_payer subimpot_a_payer {   my$this = shift;

 
 
 
 
 
 
 
 

  if( my$salaire = $this->obtenir_salaire ) {     return0.6 * $salaire;

Homme - redéfinition méthode parler

# Méthode parler (un argument) qui va redéfinir la méthode parler

# de la classe mère around 'parler' =>sub{

parler d'héritage. Ces classes vont hériter des classes "Personne" et "Activite". Pour le mentionner à Moose, c'est aussi simple que le module base.

 

héritent des attributs des classes mères, donc Moose le gère pour nous, c'est super ! Néanmoins, une femme est de sexe féminin et un homme de sexe masculin ! Il serait stupide de demander aux utilisateurs de préciser le sexe s'ils utilisaient l'une des deux classes. Il va donc falloir le définir à notre classe mère "Personne". Grâce à la fonction has de Moose, il est possible de cloner l'attribut sexe de "Personne" :

 

classe "Personne". Elle aura ainsi par défaut la valeur 'M' (ou 'F' pour la classe "Femme").

Redéfinissons maintenant notre méthode parler. Juste pour rappel, la classe "Homme" redéfinit la méthode parler de la classe "Personne" dont elle hérite.


  # Référence vers la méthode parler de la classe mère

 

Cette ligne around 'parler' => sub {}; permet de redéfinir une méthode.

Le premier argument correspond à la référence de la méthode originelle (parler). Les deux autres arguments sont l'objet ($this) et l'argument passé à la procédure.

Pour la suite, pas de changement, on utilisera toujours la pseudoclasse SUPER si besoin, ainsi que la méthode DEMOLISH (pour le destructeur). Il n'est pas nécessaire d'appeler les méthodes DEMOLISH des classes mères, Moose le fait pour nous. Voici nos classes :

Homme

 

packageHomme;    # Nom du package, de notre classe

useCarp;         # Utile pour émettre certains avertissements

usevars qw($VERSION);    # Version de notre module

$VERSION = '1.30';

 useMoose;            # Il charge automatiquement strict et warnings extends qw ( Personne Activite );    # hérite de la classe Personne et Activite

 hasbarbu => (   is=> 'rw',   isa => 'Int',   required => 0,

  reader   => 'obtenir_barbu',   writer   => 'modifier_barbu',   trigger  =>sub{

    my( $this, $barbu ) = @_;     unless( $barbu =~ /^0|1$/ ) {

      die("[Attention] Mauvais attribut barbu : $barbu [0 ou 1]\n");

    }

  },

);

# modification de l'attribut sexe hérité has'+sexe' => ( default=> 'M'); 

# Méthode parler (un argument) qui va redéfinir la méthode parler

# de la classe mère around 'parler' =>sub{

  # Référence vers la méthode parler de la classe mère

  # objet et argument

  my( $Ref_methode_parler_class, $this, $message ) = @_;

 
 
 
 
 
 
 

  unless( defined $message ) {

    $message = 'je suis un homme et Ne sais pas quoi dire';   }

$this->$Ref_methode_parler_class( $message . ' (je suis un homme)' ); 

  return1;

};

# Destructeur subDEMOLISH {   my$this = shift;

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n";

  return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

 
 

Femme

 

packageFemme;    # Nom du package, de notre classe

useCarp;         # Utile pour émettre certains avertissements

usevars qw($VERSION);    # Version de notre module

$VERSION = '1.30';

 useMoose;            # Il charge automatiquement strict et warnings extends qw ( Personne Activite );    # hérite de la classe Personne et Activite

# modification de l'attribut hérité has'+sexe' => ( default=> 'F');

# méthode accouche subaccouche {

  my( $this, $nombre_enfant ) = @_;

  if( defined $nombre_enfant ) {     unless( $nombre_enfant =~ m{^\d+$} ) {       croak "Mauvais argument : $nombre_enfant\n";       return;

    }

$this->parler("accouche de $nombre_enfant enfant(s)");

    my$NbrEnfant = $this->obtenir_nombre_enfant() + $nombre_enfant;

$this->modifier_nombre_enfant($NbrEnfant);   } 

  return;

}

# Destructeur subDEMOLISH {

 
 
 
 
 
 
 

  my$this = shift;

  # Avertissement que l'objet va mourir

  print "===================================================\n";   print "L'objet de la classe " . __PACKAGE__ . " va mourir\n"; print "===================================================\n";

   return;

}

1;    # Important, à ne pas oublier

__END__# Le compilateur ne lira pas les lignes suivantes

Voilà, je ne vous ai exposé qu'une infime partie des fonctionnalités de Moose, car pour notre exemple, c'était suffisant. Sachez qu'avec Moose, il est possible de faire beaucoup plus. Vous pouvez jeter une attention particulière sur les modules :

•    : Moose supporte les rôles qui sont des classes particulières, non instanciables ;

•    les rôles sont une alternative plus puissante aux interfaces à la Java. Ils représentent une alternative plus sûre et plus flexible à l'héritage multiple ; ? : vous y trouverez des exemples de code ; ? : les manuels Moose.

Vous       pouvez       télécharger       tous       les       scripts Moose de      cet        article. 

Voici le modèle des classes


42