Perl Language
Perl orienté objet
Recherche…
Créer des objets
Contrairement à de nombreux autres langages, Perl ne dispose pas de constructeurs qui allouent de la mémoire pour vos objets. Au lieu de cela, il convient d'écrire une méthode de classe qui à la fois crée une structure de données et la remplit avec des données (vous pouvez le connaître comme modèle de conception de méthode d'usine).
package Point;
use strict;
sub new {
my ($class, $x, $y) = @_;
my $self = { x => $x, y => $y }; # store object data in a hash
bless $self, $class; # bind the hash to the class
return $self;
}
Cette méthode peut être utilisée comme suit:
my $point = Point->new(1, 2.5);
Lorsque l'opérateur flèche ->
est utilisé avec des méthodes, son opérande gauche est ajouté à la liste d'arguments indiquée. Donc, @_
in new
contiendra des valeurs ('Point', 1, 2.5)
.
Il n'y a rien de spécial dans le nom new
. Vous pouvez appeler les méthodes d'usine comme vous préférez.
Il n'y a rien de spécial dans les hashes. Vous pouvez faire la même chose de la manière suivante:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
En général, toute référence peut être un objet, même une référence scalaire. Mais le plus souvent, les hachages constituent le moyen le plus pratique de représenter des données d'objet.
Définir des classes
En général, les classes en Perl ne sont que des packages. Ils peuvent contenir des données et des méthodes, comme les paquets habituels.
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
Il est important de noter que les variables déclarées dans un package sont des variables de classe, pas des variables d'objet (instance). La modification d'une variable au niveau du package affecte tous les objets de la classe. Comment stocker des données spécifiques à un objet, voir dans «Création d'objets».
Ce qui rend les paquets de classe spécifiques, c'est l'opérateur flèche ->
. Il peut être utilisé après un mot nu:
Point->new(...);
ou après une variable scalaire (contenant généralement une référence):
my @polar = $point->polar_coordinates;
Ce qui est à gauche de la flèche est ajouté à la liste des arguments de la méthode. Par exemple, après un appel
Point->new(1, 2);
array @_
in new
contiendra trois arguments: ('Point', 1, 2)
.
Les packages représentant des classes doivent prendre en compte cette convention et s’attendre à ce que toutes leurs méthodes aient un argument supplémentaire.
Résolution d'héritage et de méthodes
Pour faire une classe une sous-classe d'une autre classe, utilisez le pragma parent
:
package Point;
use strict;
...
1;
package Point2D;
use strict;
use parent qw(Point);
...
1;
package Point3D;
use strict;
use parent qw(Point);
...
1;
Perl permet l'héritage multiple:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
Héritage est tout au sujet de la résolution quelle méthode doit être appelée dans une situation particulière. Étant donné que pure Perl ne prescrit aucune règle concernant la structure de données utilisée pour stocker les données d'objet, l'héritage n'a rien à voir avec cela.
Considérons la hiérarchie de classes suivante:
package GeometryObject;
use strict;
sub transpose { ...}
1;
package Point;
use strict;
use parent qw(GeometryObject);
sub new { ... };
1;
package PlanarObject;
use strict;
use parent qw(GeometryObject);
sub transpose { ... }
1;
package Point2D;
use strict;
use parent qw(Point PlanarObject);
sub new { ... }
sub polar_coordinates { ... }
1;
La résolution de la méthode fonctionne comme suit:
Le point de départ est défini par l'opérande gauche de l'opérateur de flèche.
Si c'est un mot nu:
Point2D->new(...);
... ou une variable scalaire contenant une chaîne:
my $class = 'Point2D'; $class->new(...);
... alors le point de départ est le package avec le nom correspondant (
Point2D
dans les deux exemples).Si l'opérande gauche est une variable scalaire contenant une référence bénie :
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
alors le point de départ est la classe de la référence (encore une fois,
Point2D
). L'opérateur de flèche ne peut pas être utilisé pour appeler des méthodes pour les références non bénies .
Si le point de départ contient la méthode requise, il est simplement appelé.
Ainsi, depuis
Point2D::new
existe,Point2D->new(...);
l'appellera simplement
Si le point de départ ne contient pas la méthode requise, une recherche en profondeur dans les classes
parent
est effectuée. Dans l'exemple ci-dessus, l'ordre de recherche sera le suivant:-
Point2D
-
Point
(premier parent dePoint2D
) -
GeometryObject
(parent dePoint
) -
PlanarObject
(deuxième parent dePoint2D
)
Par exemple, dans le code suivant:
my $point = Point2D->new(...); $point->transpose(...);
la méthode qui sera appelée est
GeometryObject::transpose
, même si elle serait remplacée dansPlanarObject::transpose
.-
Vous pouvez définir le point de départ explicitement.
Dans l'exemple précédent, vous pouvez appeler explicitement
PlanarObject::transpose
commePlanarObject::transpose
:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
De manière similaire,
SUPER::
effectue une recherche de méthode dans les classes parentes de la classe en cours.Par exemple,
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
appellera
Point::new
au cours de l'exécution dePoint2D::new
.
Méthodes de classe et d'objet
En Perl, la différence entre les méthodes de classe (statique) et d'objet (instance) n'est pas aussi forte que dans d'autres langages, mais elle existe toujours.
L'opérande gauche de l'opérateur de flèche ->
devient le premier argument de la méthode à appeler. Ce peut être soit une chaîne:
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
ou une référence d'objet:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
Les méthodes de classe ne sont que celles qui attendent que leur premier argument soit une chaîne et que les méthodes d'objet soient celles qui s'attendent à ce que leur premier argument soit une référence d'objet.
Les méthodes de classe ne font généralement rien avec leur premier argument, qui n'est qu'un nom de classe. En général, Perl lui-même l'utilise uniquement pour la résolution de la méthode. Par conséquent, une méthode de classe typique peut également être appelée pour un objet:
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
Bien que cette syntaxe soit autorisée, elle est souvent trompeuse, il est donc préférable de l'éviter.
Les méthodes d'objet reçoivent une référence d'objet en tant que premier argument afin qu'elles puissent adresser les données d'objet (contrairement aux méthodes de classe):
package Point;
use strict;
sub polar_coordinates {
my ($point) = @_;
my $x = $point->{x};
my $y = $point->{y};
return (sqrt($x * $x + $y * $y), atan2($y, $x));
}
1;
La même méthode peut suivre les deux cas: lorsqu'elle est appelée en tant que classe ou méthode d'objet:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
Définir des classes en Perl moderne
Bien que disponible, la définition d'une classe à partir de zéro n'est pas recommandée dans Perl moderne. Utilisez l'un des systèmes OO d'assistance qui offrent plus de fonctionnalités et de commodité. Parmi ces systèmes sont:
Moose
- inspiré par le design Perl 6 OOClass::Accessor
- une alternative légère à MooseClass::Tiny
- constructeur de classes vraiment minimaliste
élan
package Foo;
use Moose;
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write boolean property
sub qux {
my $self = shift;
my $barIsBaz = $self->bar eq 'baz'; # property getter
$self->baz($barIsBaz); # property setter
}
Classe :: Accesseur (syntaxe Moose)
package Foo;
use Class::Accessor 'antlers';
has bar => (is => 'ro'); # a read-only property
has baz => (is => 'rw', isa => 'Bool'); # a read-write property (only 'is' supported, the type is ignored)
Class :: Accessor (syntaxe native)
package Foo;
use base qw(Class::Accessor);
Foo->mk_accessors(qw(bar baz)); # some read-write properties
Foo->mk_accessors(qw(qux)); # a read-only property
Classe :: Minuscule
package Foo;
use Class::Tiny qw(bar baz); # just props
Les rôles
Un rôle dans Perl est essentiellement
- un ensemble de méthodes et d'attributs qui
- injecté directement dans une classe.
Un rôle fournit une fonctionnalité qui peut être composée (ou appliquée à) toute classe (qui est dite consommer le rôle). Un rôle ne peut pas être hérité mais peut être consommé par un autre rôle.
Un rôle peut également nécessiter des classes consommatrices pour implémenter certaines méthodes au lieu d'implémenter les méthodes elles-mêmes (tout comme les interfaces Java ou C #).
Perl ne prend pas en charge les rôles, mais il existe des classes CPAN qui fournissent un tel support.
package Chatty;
use Moose::Role;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Moose;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}
Utilisez cette option si votre système OO ne prend pas en charge les rôles (par exemple, Class::Accessor
ou Class::Tiny
). Ne supporte pas les attributs.
package Chatty;
use Role::Tiny;
requires 'introduce'; # a method consuming classes must implement
sub greet { # a method already implemented in the role
print "Hi!\n";
}
package Parrot;
use Class::Tiny;
use Role::Tiny::With;
with 'Chatty';
sub introduce {
print "I'm Buddy.\n";
}