Ricerca…


Creazione di oggetti

A differenza di molti altri linguaggi, Perl non ha costruttori che allocano memoria per i propri oggetti. Invece, si dovrebbe scrivere un metodo di classe che sia creare una struttura di dati sia popolarlo con i dati (si potrebbe sapere come il modello di progettazione Metodo di fabbrica).

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

Questo metodo può essere utilizzato come segue:

my $point = Point->new(1, 2.5);

Ogni volta che l'operatore freccia -> viene utilizzato con i metodi, il suo operando di sinistra viene anteposto all'elenco di argomenti specificato. Quindi, @_ in new conterrà valori ('Point', 1, 2.5) .

Non c'è nulla di speciale nel nome new . Puoi chiamare i metodi di fabbrica come preferisci.

Non c'è niente di speciale negli hash. Potresti fare lo stesso nel modo seguente:

package Point;
use strict;

sub new {
    my ($class, @coord) = @_;
    my $self = \@coord;
    bless $self, $class;
    return $self;
}

In generale, qualsiasi riferimento può essere un oggetto, anche un riferimento scalare. Ma la maggior parte delle volte, gli hash sono il modo più conveniente per rappresentare i dati dell'oggetto.

Definizione delle classi

In generale, le classi in Perl sono solo pacchetti. Possono contenere dati e metodi, come i soliti pacchetti.

package Point;
use strict;

my $CANVAS_SIZE = [1000, 1000];

sub new {
    ...
}

sub polar_coordinates {
    ...
}

1;

È importante notare che le variabili dichiarate in un pacchetto sono variabili di classe, non variabili oggetto (istanza). La modifica di una variabile a livello di pacchetto influisce su tutti gli oggetti della classe. Come memorizzare dati specifici dell'oggetto, vedere in "Creazione di oggetti".

Ciò che rende specifici i pacchetti di classi, è l'operatore della freccia -> . Può essere usato dopo una parola nuda:

Point->new(...);

o dopo una variabile scalare (di solito contenente un riferimento):

my @polar = $point->polar_coordinates;

Quello che è a sinistra della freccia è anteposto all'elenco di argomenti del metodo. Ad esempio, dopo la chiamata

Point->new(1, 2);

array @_ in new conterrà tre argomenti: ('Point', 1, 2) .

I pacchetti che rappresentano le classi dovrebbero tenere conto di questa convenzione e si aspettano che tutti i loro metodi abbiano un argomento in più.

Ereditarietà e risoluzione dei metodi

Per fare di una classe una sottoclasse di un'altra classe, usa il 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 consente l'ereditarietà multipla:

package Point2D;
use strict;
use parent qw(Point PlanarObject);
...
1;

L'ereditarietà riguarda la risoluzione del metodo da chiamare in una situazione particolare. Poiché il puro Perl non prescrive alcuna regola sulla struttura dei dati utilizzata per archiviare i dati dell'oggetto, l'ereditarietà non ha nulla a che fare con questo.

Considera la seguente gerarchia di classi:

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 risoluzione del metodo funziona come segue:

  1. Il punto di partenza è definito dall'operando sinistro dell'operatore di frecce.

    • Se è una parola nuda:

      Point2D->new(...);
      

      ... o una variabile scalare che contiene una stringa:

      my $class = 'Point2D';
      $class->new(...);
      

      ... quindi il punto di partenza è il pacchetto con il nome corrispondente ( Point2D in entrambi gli esempi).

    • Se l'operando di sinistra è una variabile scalare che contiene un riferimento benedetto :

      my $point = {...};
      bless $point, 'Point2D'; # typically, it is encapsulated into class methods
      my @coord = $point->polar_coordinates;
      

      quindi il punto di partenza è la classe del riferimento (ancora, Point2D ). L'operatore freccia non può essere utilizzato per chiamare metodi per riferimenti non abbinati .

  2. Se il punto di partenza contiene il metodo richiesto, viene semplicemente chiamato.

    Quindi, dal momento che Point2D::new esiste,

    Point2D->new(...);
    

    semplicemente lo chiameremo.

  3. Se il punto di partenza non contiene il metodo richiesto, viene eseguita la ricerca di profondità nelle classi parent . Nell'esempio sopra, l'ordine di ricerca sarà il seguente:

    • Point2D
    • Point (primo genitore di Point2D )
    • GeometryObject (parent di Point )
    • PlanarObject (secondo genitore di Point2D )

    Ad esempio, nel seguente codice:

    my $point = Point2D->new(...);
    $point->transpose(...);
    

    il metodo che verrà chiamato è GeometryObject::transpose , anche se verrà sovrascritto in PlanarObject::transpose .

  4. È possibile impostare il punto di partenza in modo esplicito.

    Nell'esempio precedente, puoi chiamare in modo esplicito PlanarObject::transpose modo:

    my $point = Point2D->new(...);
    $point->PlanarObject::transpose(...);
    
  5. In modo simile, SUPER:: esegue la ricerca del metodo nelle classi parent della classe corrente.

    Per esempio,

    package Point2D;
    use strict;
    use parent qw(Point PlanarObject);
    
    sub new {
        (my $class, $x, $y) = @_;
        my $self = $class->SUPER::new;
        ...
    }
    
    1;
    

    chiamerà Point::new nel corso di Point2D::new esecuzione.

Metodi di classe e oggetti

In Perl, la differenza tra i metodi di classe (statico) e oggetto (istanza) non è così forte come in altri linguaggi, ma esiste ancora.

L'operando di sinistra dell'operatore di frecce -> diventa il primo argomento del metodo da chiamare. Potrebbe essere una stringa:

# the first argument of new is string 'Point' in both cases
Point->new(...);

my $class = 'Point';
$class->new(...);

o un riferimento a un oggetto:

# reference contained in $point is the first argument of polar_coordinates
my $point = Point->new(...);
my @coord = $point->polar_coordinates;

I metodi di classe sono solo quelli che si aspettano che il loro primo argomento sia una stringa, ei metodi oggetto sono quelli che si aspettano che il loro primo argomento sia un riferimento a un oggetto.

I metodi di classe in genere non fanno nulla con il loro primo argomento, che è solo un nome della classe. Generalmente, è usato solo da Perl stesso per la risoluzione del metodo. Pertanto, un tipico metodo di classe può essere chiamato anche per un oggetto:

my $width = Point->canvas_width;

my $point = Point->new(...);
my $width = $point->canvas_width;

Sebbene questa sintassi sia consentita, spesso è fuorviante, quindi è meglio evitarlo.

I metodi Object ricevono un riferimento oggetto come primo argomento, in modo che possano indirizzare i dati dell'oggetto (diversamente dai metodi di 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;

Lo stesso metodo è in grado di tenere traccia di entrambi i casi: quando viene chiamato come una classe o un metodo oggetto:

sub universal_method {
    my $self = shift;
    if (ref $self) {
        # object logic
        ...
    }
    else {
        # class logic
        ...
    }
}

Definire le classi nel moderno Perl

Sebbene disponibile, la definizione di una classe da zero non è raccomandata nel moderno Perl. Utilizza uno dei sistemi helper OO che offrono più funzionalità e praticità. Tra questi sistemi sono:

alce americano

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
}

Class :: Accessor (sintassi alci)

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 (sintassi nativa)

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 :: Piccolo

package Foo;
use Class::Tiny qw(bar baz);  # just props

ruoli

Un ruolo in Perl è essenzialmente

  • un insieme di metodi e attributi che
  • iniettato direttamente in una classe.

Un ruolo fornisce una parte di funzionalità che può essere composta (o applicata a) qualsiasi classe (che si dice consuma il ruolo). Un ruolo non può essere ereditato ma può essere utilizzato da un altro ruolo.

Un ruolo può anche richiedere che le classi di consumo implementino alcuni metodi invece di implementare i metodi stessi (proprio come le interfacce in Java o C #).

Perl non ha il supporto integrato per i ruoli, ma ci sono classi CPAN che forniscono tale supporto.

Moose :: Ruolo

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

Ruolo :: Piccolo

Utilizzare se il sistema OO non supporta i ruoli (ad es. Class::Accessor o Class::Tiny ). Non supporta gli attributi.

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow