Perl Language
Obiektowy Perl
Szukaj…
Tworzenie obiektów
W przeciwieństwie do wielu innych języków, Perl nie ma konstruktorów, które przydzielają pamięć dla twoich obiektów. Zamiast tego należy napisać metodę klasową, która zarówno tworzy strukturę danych, jak i zapełnia ją danymi (możesz to nazwać wzorcem projektowym metody fabrycznej).
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;
}
Z tej metody można korzystać w następujący sposób:
my $point = Point->new(1, 2.5);
Ilekroć operator strzałki ->
jest używany z metodami, jego lewy operand jest dołączany do podanej listy argumentów. Tak więc @_
w new
będzie zawierać wartości ('Point', 1, 2.5)
.
W nazwie new
nie ma nic specjalnego. Możesz wywoływać metody fabryczne według własnego uznania.
W hashach nie ma nic specjalnego. Możesz zrobić to samo w następujący sposób:
package Point;
use strict;
sub new {
my ($class, @coord) = @_;
my $self = \@coord;
bless $self, $class;
return $self;
}
Zasadniczo każde odniesienie może być obiektem, nawet odniesieniem skalarnym. Ale najczęściej skróty są najwygodniejszym sposobem przedstawiania danych obiektowych.
Definiowanie klas
Ogólnie rzecz biorąc, klasy w Perlu są tylko pakietami. Mogą zawierać dane i metody, jak zwykle pakiety.
package Point;
use strict;
my $CANVAS_SIZE = [1000, 1000];
sub new {
...
}
sub polar_coordinates {
...
}
1;
Należy zauważyć, że zmienne zadeklarowane w pakiecie są zmiennymi klas, a nie zmiennymi obiektowymi (instancjami). Zmiana zmiennej na poziomie pakietu wpływa na wszystkie obiekty klasy. Jak przechowywać dane specyficzne dla obiektu, patrz „Tworzenie obiektów”.
Specyfiką pakietów klasowych jest operator strzałki ->
. Można go użyć po nagim słowie:
Point->new(...);
lub po zmiennej skalarnej (zwykle zawierającej referencję):
my @polar = $point->polar_coordinates;
To, co jest po lewej stronie strzałki, jest dołączane do podanej listy argumentów metody. Na przykład po połączeniu
Point->new(1, 2);
tablica @_
w new
będzie zawierała trzy argumenty: ('Point', 1, 2)
.
Pakiety reprezentujące klasy powinny brać pod uwagę tę konwencję i oczekiwać, że wszystkie ich metody będą miały jeden dodatkowy argument.
Rozwiązywanie dziedziczenia i metody
Aby uczynić klasę podklasą innej klasy, użyj parent
pragmy:
package Point;
use strict;
...
1;
package Point2D;
use strict;
use parent qw(Point);
...
1;
package Point3D;
use strict;
use parent qw(Point);
...
1;
Perl pozwala na wielokrotne dziedziczenie:
package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;
Dziedziczenie polega na rozwiązaniu, którą metodę należy wywołać w konkretnej sytuacji. Ponieważ czysty Perl nie określa żadnych reguł dotyczących struktury danych używanych do przechowywania danych obiektowych, dziedziczenie nie ma z tym nic wspólnego.
Rozważ następującą hierarchię klas:
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;
Rozdzielczość metody działa w następujący sposób:
Punkt początkowy jest zdefiniowany przez lewy operand operatora strzałki.
Jeśli jest to gołe słowo:
Point2D->new(...);
... lub zmienna skalarna zawierająca ciąg:
my $class = 'Point2D'; $class->new(...);
... wtedy punktem początkowym jest pakiet o odpowiedniej nazwie (
Point2D
w obu przykładach).Jeśli lewy operand jest zmienną skalarną zawierającą błogosławione odniesienie:
my $point = {...}; bless $point, 'Point2D'; # typically, it is encapsulated into class methods my @coord = $point->polar_coordinates;
wtedy punktem początkowym jest klasa odniesienia (ponownie
Point2D
). Operatora strzałek nie można używać do wywoływania metod dla niepowiązanych odniesień.
Jeśli punkt początkowy zawiera wymaganą metodę, jest ona po prostu wywoływana.
Zatem, ponieważ istnieje
Point2D::new
,Point2D->new(...);
po prostu to nazwie.
Jeśli punkt początkowy nie zawiera wymaganej metody, wykonywane jest wyszukiwanie w pierwszej kolejności w klasach
parent
. W powyższym przykładzie kolejność wyszukiwania będzie następująca:-
Point2D
-
Point
(pierwszy rodzicPoint2D
) -
GeometryObject
(rodzicPoint
) -
PlanarObject
(drugiPoint2D
nadrzędnyPoint2D
)
Na przykład w następującym kodzie:
my $point = Point2D->new(...); $point->transpose(...);
metoda, która zostanie wywołana, to
GeometryObject::transpose
, nawet jeśli zostanie zastąpiona wPlanarObject::transpose
.-
Możesz jawnie ustawić punkt początkowy.
W poprzednim przykładzie możesz jawnie wywołać
PlanarObject::transpose
sposób:my $point = Point2D->new(...); $point->PlanarObject::transpose(...);
W podobny sposób
SUPER::
wykonuje wyszukiwanie metod w klasach nadrzędnych bieżącej klasy.Na przykład,
package Point2D; use strict; use parent qw(Point PlanarObject); sub new { (my $class, $x, $y) = @_; my $self = $class->SUPER::new; ... } 1;
wywoła
Point::new
w trakciePoint2D::new
wykonania.
Metody klasowe i obiektowe
W Perlu różnica między metodami klasowymi (statycznymi) a obiektowymi (instancjami) nie jest tak silna jak w niektórych innych językach, ale nadal istnieje.
Lewy operand operatora strzałki ->
staje się pierwszym argumentem wywoływanej metody. Może to być ciąg znaków:
# the first argument of new is string 'Point' in both cases
Point->new(...);
my $class = 'Point';
$class->new(...);
lub odwołanie do obiektu:
# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;
Metody klasowe to tylko te, które oczekują, że pierwszy argument będzie łańcuchem, a metody obiektowe, które oczekują, że pierwszy argument będzie odwołaniem do obiektu.
Metody klasowe zwykle nie robią nic z pierwszym argumentem, który jest tylko nazwą klasy. Zasadniczo jest on używany tylko przez samego Perla do rozwiązywania metod. Dlatego dla obiektu można również wywołać typową metodę klasową:
my $width = Point->canvas_width;
my $point = Point->new(...);
my $width = $point->canvas_width;
Chociaż ta składnia jest dozwolona, często wprowadza w błąd, dlatego lepiej jej unikać.
Metody obiektowe otrzymują odwołanie do obiektu jako pierwszy argument, dzięki czemu mogą adresować dane obiektowe (w przeciwieństwie do metod klasowych):
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;
Ta sama metoda może śledzić oba przypadki: gdy jest wywoływana jako metoda klasy lub obiektu:
sub universal_method {
my $self = shift;
if (ref $self) {
# object logic
...
}
else {
# class logic
...
}
}
Definiowanie klas we współczesnym Perlu
Chociaż dostępne, definiowanie klasy od zera nie jest zalecane we współczesnym Perlu. Użyj jednego z pomocniczych systemów OO, który zapewnia więcej funkcji i wygodę. Wśród tych systemów są:
Moose
- inspirowany projektem Perla 6 OOClass::Accessor
- lekka alternatywa dla ŁosiaClass::Tiny
- naprawdę minimalny konstruktor klas
Łoś
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
}
Klasa :: Akcesorium (składnia Łosia)
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 (natywna składnia)
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
Klasa :: Mały
package Foo;
use Class::Tiny qw(bar baz); # just props
Role
Rola w Perlu jest zasadniczo
- zestaw metod i atrybutów, które
- wstrzykiwany bezpośrednio do klasy.
Rola stanowi kawałek funkcji, która może składać się na (lub stosowany do) każdej klasy (które, jak stwierdzono, zużywają roli). Rola nie może być dziedziczona, ale może zostać wykorzystana przez inną rolę.
Rola może również wymagać użycia klas do implementacji niektórych metod zamiast implementacji samych metod (podobnie jak interfejsy w Javie lub C #).
Perl nie ma wbudowanej obsługi ról, ale istnieją klasy CPAN, które zapewniają taką obsługę.
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";
}
Użyj, jeśli twój system OO nie obsługuje ról (np. Class::Accessor
lub Class::Tiny
). Nie obsługuje atrybutów.
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";
}