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:

  1. 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ń.

  2. 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.

  3. 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 rodzic Point2D )
    • GeometryObject (rodzic Point )
    • PlanarObject (drugi Point2D nadrzędny Point2D )

    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 w PlanarObject::transpose .

  4. 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(...);
    
  5. 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 trakcie Point2D::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ą:

Ł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ę.

Łoś :: Rola

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";
}

Rola :: Mały

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";
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow