Buscar..


Creando objetos

A diferencia de muchos otros idiomas, Perl no tiene constructores que asignen memoria para sus objetos. En su lugar, uno debería escribir un método de clase que cree una estructura de datos y lo rellene con datos (puede que lo conozca como el patrón de diseño del Método de fábrica).

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

Este método se puede utilizar de la siguiente manera:

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

Siempre que el operador de flecha -> se usa con métodos, su operando izquierdo se antepone a la lista de argumentos dada. Entonces, @_ en new contendrá valores ('Point', 1, 2.5) .

No hay nada especial en el nombre new . Puede llamar a los métodos de fábrica como prefiera.

No hay nada especial en los hashes. Podrías hacer lo mismo de la siguiente manera:

package Point;
use strict;

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

En general, cualquier referencia puede ser un objeto, incluso una referencia escalar. Pero la mayoría de las veces, los hashes son la forma más conveniente de representar datos de objetos.

Definiendo clases

En general, las clases en Perl son solo paquetes. Pueden contener datos y métodos, como los paquetes habituales.

package Point;
use strict;

my $CANVAS_SIZE = [1000, 1000];

sub new {
    ...
}

sub polar_coordinates {
    ...
}

1;

Es importante tener en cuenta que las variables declaradas en un paquete son variables de clase, no variables de objeto (instancia). El cambio de una variable a nivel de paquete afecta a todos los objetos de la clase. Cómo almacenar datos específicos de objetos, consulte "Crear objetos".

Lo que hace que los paquetes de clase sean específicos, es el operador de flecha -> . Puede ser usado después de una sola palabra:

Point->new(...);

o después de una variable escalar (generalmente con una referencia):

my @polar = $point->polar_coordinates;

Lo que está a la izquierda de la flecha se añade a la lista de argumentos dada del método. Por ejemplo, después de la llamada

Point->new(1, 2);

array @_ en new contendrá tres argumentos: ('Point', 1, 2) .

Los paquetes que representan clases deben tener en cuenta esta convención y esperar que todos sus métodos tengan un argumento adicional.

Herencia y resolución de métodos.

Para hacer que una clase sea una subclase de otra clase, use 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 permite la herencia múltiple:

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

La herencia tiene que ver con la resolución de qué método debe llamarse en una situación particular. Dado que Perl puro no prescribe ninguna regla sobre la estructura de datos utilizada para almacenar datos de objetos, la herencia no tiene nada que ver con eso.

Considere la siguiente jerarquía de clases:

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 resolución del método funciona de la siguiente manera:

  1. El punto de partida está definido por el operando izquierdo del operador de flecha.

    • Si es una palabra simple:

      Point2D->new(...);
      

      ... o una variable escalar sosteniendo una cadena:

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

      ... entonces el punto de partida es el paquete con el nombre correspondiente ( Point2D en ambos ejemplos).

    • Si el operando izquierdo es una variable escalar que contiene una referencia bendita :

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

      entonces el punto de partida es la clase de la referencia (nuevamente, Point2D ). El operador de flecha no se puede utilizar para llamar a métodos para referencias no bendecidas .

  2. Si el punto de partida contiene el método requerido, simplemente se llama.

    Así, puesto que Point2D::new existe,

    Point2D->new(...);
    

    simplemente lo llamará.

  3. Si el punto de partida no contiene el método requerido, se realiza una búsqueda en profundidad en las clases parent . En el ejemplo anterior, el orden de búsqueda será el siguiente:

    • Point2D
    • Point (primer padre de Point2D )
    • GeometryObject (padre de Point )
    • PlanarObject (segundo padre de Point2D )

    Por ejemplo, en el siguiente código:

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

    el método que se llamará es GeometryObject::transpose , aunque se anularía en PlanarObject::transpose .

  4. Puede establecer el punto de partida explícitamente.

    En el ejemplo anterior, puede llamar explícitamente a PlanarObject::transpose así:

    my $point = Point2D->new(...);
    $point->PlanarObject::transpose(...);
    
  5. De manera similar, SUPER:: realiza la búsqueda de métodos en las clases primarias de la clase actual.

    Por ejemplo,

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

    Point2D::new Point::new en el curso de la Point2D::new ejecución de Point2D::new .

Métodos de clase y objeto

En Perl, la diferencia entre los métodos de clase (estática) y objeto (instancia) no es tan fuerte como en otros lenguajes, pero aún existe.

El operando izquierdo del operador de flecha -> convierte en el primer argumento del método a llamar. Puede ser una cadena:

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

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

o una referencia de objeto:

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

Los métodos de clase son solo los que esperan que su primer argumento sea una cadena, y los métodos de objeto son los que esperan que su primer argumento sea una referencia de objeto.

Los métodos de clase normalmente no hacen nada con su primer argumento, que es solo un nombre de la clase. En general, Perl solo lo utiliza para la resolución de métodos. Por lo tanto, también se puede llamar a un método de clase típico para un objeto:

my $width = Point->canvas_width;

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

Aunque esta sintaxis está permitida, a menudo es engañosa, por lo que es mejor evitarla.

Los métodos de objeto reciben una referencia de objeto como primer argumento, por lo que pueden abordar los datos del objeto (a diferencia de los métodos de clase):

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;

El mismo método puede rastrear ambos casos: cuando se llama como una clase o un método de objeto:

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

Definiendo clases en Perl moderno.

Aunque está disponible, la definición de una clase desde cero no se recomienda en Perl moderno. Utilice uno de los sistemas OO auxiliares que proporcionan más características y conveniencia. Entre estos sistemas se encuentran:

Alce

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
}

Clase :: Accessor (sintaxis de alces)

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)

Clase :: Accessor (sintaxis 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

Clase :: Tiny

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

Roles

Un papel en Perl es esencialmente

  • un conjunto de métodos y atributos que
  • inyectado en una clase directamente.

Un rol proporciona una pieza de funcionalidad que se puede componer (o aplicar ) a cualquier clase (que se dice que consume el rol). Un rol no puede ser heredado, pero puede ser consumido por otro rol.

Un rol también puede requerir clases de consumo para implementar algunos métodos en lugar de implementar los métodos en sí (como las interfaces en Java o C #).

Perl no tiene soporte incorporado para roles, pero hay clases de CPAN que proporcionan dicho soporte.

Moose :: Role

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

Rol :: Diminuto

Utilícelo si su sistema OO no proporciona soporte para roles (por ejemplo, Class::Accessor o Class::Tiny ) No soporta atributos.

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow