Jouons avec Moose (ou comment réapprendre le Perl Objet) - partie 4
Cet article est la quatrième partie d’une série consacrée à Moose qui vise à montrer comment écrire du Perl Objet moderne. Les exemples de code sont tous testés avec l’excellent shell Devel::REPL.
Dans notre précédent article, nous avons vu comment utiliser les Moose::Role pour définir des armes. Ajourd’hui nous allons créer une nouvelle classe de héros, les mages, qui vont nous servir à illustrer un concept tout aussi magique : la coercition (coercion en anglais). Cet article est dédicacé à Jérôme Bourgeois qui m’a donné l’idée de faire intervenir un mage dans cette série, merci à lui.
Nous allons aujourd’hui créer la classe « Mage » qui modélisera un nouveau type de héros. Pour commencer, et avant de s’attaquer à la coercition, nous allons spécialiser la classe Mage. Nous voulons qu’un mage soit un héros comme un autre à ceci prêt qu’il est plus chétif (c’est bien connu, les mages sont tous végétariens, ça facilite la circulation des fluides énergétiques, mais ça freine sensiblement le développement de la masse musculaire).
Bref. Un mage est un Héros disions-nous :
package Mage; use Moose; extends 'Heros';
Mais un mage est plus faible qu’un héros conventionnel, nous voulons donc altérer sa force et son endurance. Pour cela, Moose nous permet de modifier un attribut hérité en préfixant le nom dudit attribut par un “+” :
has '+force' => (default => 2); has '+endurance' => (default => 2);
Les attributs force et endurance hérités de la classe Héros (définie dans le permiet article de cette série) vaudront donc 2 pour un Mage, au lieu de 3.
Perl> $merlin = new Mage name => "Merlin" $Mage1 = Mage=HASH(0x8b082c4); Perl> $merlin->force 2
Maintenant, nous allons enrichir le mage en le dottant d’un familier (pour nos amis non-rôliste, rapellons que les familiers sont des petites créatures qui accompagnent les mages). Pour cela, nous allons faire intervenir le mécanisme de coercition, en créant un attribut « familier ».
La coercition va nous permettre de donner à l’accesseur de l’attribut familier une chaîne en entrée et celui-ci se chargera de convertir la chaîne en un objet Heros.
Pour cela, il nous faut définir un type de champ, et cela se fera grâce qu module Moose::Util::TypeConstraints :
package Mage;
use Moose;
use Moose::Util::TypeConstraints;
use Heros;
subtype 'Familier'
=> as 'Object'
=> where {$_->isa('Heros')};
Maintenant nous disposons d’un type d’attribut nommé « Familier » qui cache un objet de la classe « Heros ».
Nous allon à présent définir la coercition qui doit s’opérer lorsqu’un attribut de ce type est modifié avec une chaîne de caractères :
coerce 'Familier'
=> from 'Str'
=> via {new Heros
name => $_,
force => 1,
endurance => 1};
Nous convertissons donc la chaîne donnée en Heros dont le nom est précisément la chaîne fournie. Au passage, comme le familier est une petite créature, nous écrasons les valeurs par défaut du Héros en force et endurance. Le lecteur comprendra ici que nous pourrions définir une classe Familier pour l’occasion.
Maintenant, il nous reste à déclarer l’attribut à proprement parlé :
has 'familier' => (
is => 'rw',
isa => 'Familier',
coerce => 1
);
On note la présence du mot-clef “coerce” ici qui active la coercition. Ce mécanisme étant désactivé par défaut pour éviter les mauvaises surprises.
Bien. Vérifions que tout se comporte comme prévu :
Perl> $merlin->familier('sarkopette')
$Heros1 = Heros=HASH(0x8b2bb8c);
$merlin->familier->force
1
$merlin->familier->name
sarkopette
Le coercition a bien eu lieu, lorsque l’accesseur familier() est utilisé, une instanciation de Heros a lieu, comme par magie.