Perl Language
Objectgeoriënteerde Perl
Zoeken…
Objecten maken
In tegenstelling tot veel andere talen heeft Perl geen constructors die geheugen toewijzen aan uw objecten. In plaats daarvan moet men een klassemethode schrijven die zowel een gegevensstructuur maakt als deze met gegevens vult (u kent deze misschien als het ontwerppatroon van de fabrieksmethode).
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;
}
Deze methode kan als volgt worden gebruikt:
my $point = Point->new(1, 2.5);
Telkens wanneer de pijloperator ->
wordt gebruikt met methoden, wordt de linkeroperand toegevoegd aan de gegeven argumentenlijst. Dus, @_
in new
zal waarden bevatten ('Point', 1, 2.5)
.
Er is niets bijzonders in de naam new
. U kunt de fabrieksmethoden naar wens gebruiken.
Er is niets bijzonders in hashes. Je zou hetzelfde kunnen doen op de volgende manier:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
In het algemeen kan elke verwijzing een object zijn, zelfs een scalaire verwijzing. Maar meestal zijn hashes de handigste manier om objectgegevens weer te geven.
Klassen definiëren
Over het algemeen zijn klassen in Perl slechts pakketten. Ze kunnen gegevens en methoden bevatten, zoals gebruikelijk pakketten.
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
Het is belangrijk op te merken dat de in een pakket gedeclareerde variabelen klassenvariabelen zijn, geen object (instantie) variabelen. Het wijzigen van een variabele op pakketniveau is van invloed op alle objecten van de klasse. Zie "Objecten maken" voor informatie over het opslaan van objectspecifieke gegevens.
Wat klassepakketten specifiek maakt, is de pijloperator ->
. Het kan worden gebruikt na een duidelijk woord:
Point->new(...);
of na een scalaire variabele (meestal met een referentie):
my @polar = $point->polar_coordinates;
Wat zich links van de pijl bevindt, wordt voorafgegaan aan de gegeven argumentenlijst van de methode. Bijvoorbeeld na een oproep
Point->new(1, 2);
array @_
in new
zal drie argumenten bevatten: ('Point', 1, 2)
.
Pakketten die klassen vertegenwoordigen, moeten rekening houden met deze conventie en verwachten dat al hun methoden één extra argument hebben.
Overerving en resolutie van methoden
Gebruik parent
pragma om van een klasse een subklasse van een andere klasse te maken:
package Point;
use strict;
...
1;
package Point2D;
use strict;
use parent qw(Point);
...
1;
package Point3D;
use strict;
use parent qw(Point);
...
1;
Perl zorgt voor meervoudige overerving:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
Bij overerving draait alles om resolutie welke methode in een bepaalde situatie moet worden genoemd. Aangezien pure Perl geen regels voorschrijft over de gegevensstructuur die wordt gebruikt om objectgegevens op te slaan, heeft overerving daar niets mee te maken.
Overweeg de volgende klassenhiërarchie:
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;
De methode resolutie werkt als volgt:
Het startpunt wordt bepaald door de linkeroperand van de pijloperator.
Als het een kaal woord is:
Point2D->new(...);
... of een scalaire variabele met een string:
my $class = 'Point2D'; $class->new(...);
... dan is het startpunt het pakket met de bijbehorende naam (
Point2D
in beide voorbeelden).Als de linkeroperand een scalaire variabele is met een gezegende referentie:
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
dan is het startpunt de klasse van de referentie (nogmaals,
Point2D
). De pijloperator kan niet worden gebruikt om methoden voor ongetemde verwijzingen aan te roepen.
Als het startpunt de vereiste methode bevat, wordt deze eenvoudigweg aangeroepen.
Aangezien
Point2D::new
bestaat,Point2D->new(...);
zal het gewoon noemen.
Als het startpunt niet de vereiste methode bevat, wordt diepte-eerste zoekopdracht in de
parent
klassen uitgevoerd. In het bovenstaande voorbeeld is de zoekvolgorde als volgt:-
Point2D
-
Point
(eerste ouder vanPoint2D
) -
GeometryObject
(ouder vanPoint
) -
PlanarObject
(tweede ouder vanPoint2D
)
Bijvoorbeeld in de volgende code:
my $point = Point2D->new(...); $point->transpose(...);
de methode die wordt aangeroepen is
GeometryObject::transpose
, ook al zou deze worden overschreven inPlanarObject::transpose
.-
U kunt het startpunt expliciet instellen.
In het vorige voorbeeld kunt u
PlanarObject::transpose
als volgt expliciet aanroepen:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
Op een vergelijkbare manier voert
SUPER::
methoden uit in bovenliggende klassen van de huidige klasse.Bijvoorbeeld,
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
roept
Point::new
in de loop van dePoint2D::new
uitvoering.
Klasse- en objectmethoden
In Perl is het verschil tussen de methoden class (statisch) en object (instantie) niet zo sterk als in sommige andere talen, maar het bestaat nog steeds.
De linkeroperand van de pijloperator ->
wordt het eerste argument van de aan te roepen methode. Het kan een string zijn:
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
of een objectverwijzing:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
Klasse methoden zijn gewoon degenen die verwachten dat hun eerste argument een string is, en object methoden zijn degenen die verwachten dat hun eerste argument een objectverwijzing is.
Class-methoden doen meestal niets met hun eerste argument, wat slechts een naam van de class is. Over het algemeen wordt het alleen door Perl zelf gebruikt voor het oplossen van methoden. Daarom kan ook een typische klassemethode voor een object worden aangeroepen:
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
Hoewel deze syntaxis is toegestaan, is deze vaak misleidend, dus het is beter om deze te vermijden.
Objectmethoden ontvangen een objectverwijzing als het eerste argument, zodat ze de objectgegevens kunnen adresseren (in tegenstelling tot klassemethoden):
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;
Dezelfde methode kan beide gevallen volgen: wanneer deze wordt aangeroepen als een klasse of een objectmethode:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
Definiëren van klassen in moderne Perl
Hoewel beschikbaar, het definiëren van een klasse van de grond wordt niet aanbevolen in de moderne Perl. Gebruik een van de helper OO-systemen die meer functies en gemak bieden. Onder deze systemen zijn:
Moose
- geïnspireerd door het ontwerp van Perl 6 OOClass::Accessor
- een lichtgewicht alternatief voor MooseClass::Tiny
- echt minimale klassenbouwer
eland
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
}
Klasse :: Accessor (syntaxis van 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)
Klasse :: Accessor (native syntaxis)
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
Class :: Tiny
package Foo;
use Class::Tiny qw(bar baz); # just props
Rollen
Een rol in Perl is in wezen
- een set methoden en attributen die
- rechtstreeks in een klas geïnjecteerd.
Een rol biedt een stuk functionaliteit dat kan worden gecomponeerd in (of toegepast op) elke klasse (waarvan wordt gezegd dat deze de rol verbruikt ). Een rol kan niet worden geërfd, maar kan worden gebruikt door een andere rol.
Een rol kan ook vereisen dat klassen worden gebruikt om sommige methoden te implementeren in plaats van de methoden zelf te implementeren (net als interfaces in Java of C #).
Perl heeft geen ingebouwde ondersteuning voor rollen, maar er zijn CPAN-klassen die dergelijke ondersteuning bieden.
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";
}
Gebruik als uw OO-systeem geen ondersteuning voor rollen biedt (bijvoorbeeld Class::Accessor
of Class::Tiny
). Ondersteunt geen attributen.
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";
}