Perl Language
Objektorienterad Perl
Sök…
Skapa objekt
Till skillnad från många andra språk har Perl inte konstruktörer som tilldelar minne för dina objekt. Istället bör man skriva en klassmetod som både skapar en datastruktur och fyller den med data (du kanske känner till det som fabriksmetodens designmönster).
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;
}
Denna metod kan användas enligt följande:
my $point = Point->new(1, 2.5);
Varje gång piloperatören ->
används med metoder förinställs dess vänstra operand till den angivna argumentlistan. Så, @_
i new
kommer att innehålla värden ('Point', 1, 2.5)
.
Det är inget speciellt i namnet new
. Du kan ringa fabriksmetoderna som du föredrar.
Det finns inget speciellt med hascher. Du kan göra samma sak på följande sätt:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
I allmänhet kan varje referens vara ett objekt, till och med en skalreferens. Men oftast är hash det mest praktiska sättet att representera objektdata.
Definiera klasser
I allmänhet är klasser i Perl bara paket. De kan innehålla data och metoder, som vanliga paket.
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
Det är viktigt att notera att de variabler som deklareras i ett paket är klassvariabler, inte objekt (instans) variabler. Ändring av en variabel på paketnivå påverkar alla objekt i klassen. Hur du lagrar objektspecifik data, se i "Skapa objekt".
Det som gör klasspaket specifika är piloperatören ->
. Det kan användas efter ett blott ord:
Point->new(...);
eller efter en skalärvariabel (vanligtvis med en referens):
my @polar = $point->polar_coordinates;
Det som är till vänster om pilen förhindras till den angivna argumentlistan för metoden. Till exempel efter samtal
Point->new(1, 2);
array @_
i new
kommer att innehålla tre argument: ('Point', 1, 2)
.
Paket som representerar klasser bör ta hänsyn till denna konvention och förvänta sig att alla deras metoder kommer att ha ett extra argument.
Arv och metodupplösning
För att göra en klass till en underklass av en annan klass använder du 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 möjliggör flera arv:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
Arv handlar om upplösning vilken metod som ska kallas i en viss situation. Eftersom ren Perl inte föreskriver några regler om den datastruktur som används för att lagra objektdata har arv ingenting att göra med det.
Tänk på följande klasshierarki:
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;
Metodupplösningen fungerar enligt följande:
Startpunkten definieras av piloperatörens vänstra operand.
Om det är ett blott ord:
Point2D->new(...);
... eller en skalärvariabel med en sträng:
my $class = 'Point2D'; $class->new(...);
... då är utgångspunkten paketet med motsvarande namn (
Point2D
i båda exemplen).Om den vänstra operanden är en skalvariabel med en välsignad referens:
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
då är utgångspunkten klassens referens (igen,
Point2D
). Piloperatören kan inte användas för att ringa metoder för oblankade referenser.
Om utgångspunkten innehåller den nödvändiga metoden, kallas den helt enkelt.
Point2D::new
existerar,Point2D->new(...);
kommer helt enkelt att kalla det.
Om startpunkten inte innehåller den erforderliga metoden utförs djup-först-sökning i
parent
klasser. I exemplet ovan kommer sökordningen att vara följande:-
Point2D
-
Point
(första förälder tillPoint2D
) -
GeometryObject
(moder tillPoint
) -
PlanarObject
(andra förälder tillPoint2D
)
Till exempel i följande kod:
my $point = Point2D->new(...); $point->transpose(...);
metoden som kommer att kallas är
GeometryObject::transpose
, även om den skulle åsidosättas iPlanarObject::transpose
.-
Du kan ange utgångspunkten uttryckligen.
I föregående exempel kan du uttryckligen ringa
PlanarObject::transpose
så:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
På liknande sätt utför
SUPER::
metodsökning i överordnade klasser i den aktuella klassen.Till exempel,
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
kommer att ringa
Point::new
under loppet avPoint2D::new
exekvering.
Klass- och objektmetoder
I Perl är skillnaden mellan klass (statisk) och objekt (instans) -metoder inte så stark som på vissa andra språk, men de finns fortfarande.
Den vänstra operanden för piloperatören ->
blir det första argumentet för metoden som ska kallas. Det kan vara antingen en sträng:
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
eller en objektreferens:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
Klassmetoder är bara de som förväntar sig att deras första argument ska vara en sträng, och objektmetoder är de som förväntar sig att deras första argument är en objektreferens.
Klassmetoder gör vanligtvis ingenting med sitt första argument, som bara är klassens namn. I allmänhet används det bara av Perl själv för metodupplösning. Därför kan en typisk klassmetod också kallas för ett objekt:
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
Även om denna syntax är tillåten är den ofta vilseledande, så det är bättre att undvika det.
Objektmetoder får en objektreferens som det första argumentet, så att de kan adressera objektdata (till skillnad från klassmetoder):
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;
Samma metod kan spåra båda fallen: när det kallas som en klass eller en objektmetod:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
Definiera klasser i moderna Perl
Även om det finns tillgängligt rekommenderas inte att definiera en klass från början i moderna Perl. Använd ett av hjälp-OO-system som ger fler funktioner och bekvämlighet. Bland dessa system är:
Moose
- inspirerad av Perl 6 OO-designClass::Accessor
- ett lätt alternativ till MooseClass::Tiny
- verkligen minimal klassbyggare
Älg
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
}
Klass :: Accessor (Moose syntax)
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)
Klass :: Accessor (inbyggd syntax)
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
Klass :: Tiny
package Foo;
use Class::Tiny qw(bar baz); # just props
roller
En roll i Perl är i huvudsak
- en uppsättning metoder och attribut som
- injiceras direkt i en klass.
En roll tillhandahåller en bit av funktionalitet som kan komponeras i (eller tillämpas på) varje klass (som sägs konsumera rollen). En roll kan inte ärvas men kan konsumeras av en annan roll.
En roll kan också kräva konsumtionsklasser för att implementera vissa metoder istället för att implementera själva metoderna (precis som gränssnitt i Java eller C #).
Perl har inte inbyggt stöd för roller men det finns CPAN-klasser som ger sådant stöd.
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";
}
Använd om ditt OO-system inte ger stöd för roller (t.ex. Class::Accessor
eller Class::Tiny
). Stödjer inte attribut.
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";
}