Sök…


Skapa objekt

Till skillnad från många andra språk har Perl inte konstruktörer som tilldelar minne för dina objekt. Istället bör man skriva en klassmetod som både skapar en datastruktur och fyller den med data (du kanske känner till det som fabriksmetodens designmönster).

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

Denna metod kan användas enligt följande:

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

Varje gång piloperatören -> används med metoder förinställs dess vänstra operand till den angivna argumentlistan. Så, @_ i new kommer att innehålla värden ('Point', 1, 2.5) .

Det är inget speciellt i namnet new . Du kan ringa fabriksmetoderna som du föredrar.

Det finns inget speciellt med hascher. Du kan göra samma sak på följande sätt:

package Point;
use strict;

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

I allmänhet kan varje referens vara ett objekt, till och med en skalreferens. Men oftast är hash det mest praktiska sättet att representera objektdata.

Definiera klasser

I allmänhet är klasser i Perl bara paket. De kan innehålla data och metoder, som vanliga paket.

package Point;
use strict;

my $CANVAS_SIZE = [1000, 1000];

sub new {
    ...
}

sub polar_coordinates {
    ...
}

1;

Det är viktigt att notera att de variabler som deklareras i ett paket är klassvariabler, inte objekt (instans) variabler. Ändring av en variabel på paketnivå påverkar alla objekt i klassen. Hur du lagrar objektspecifik data, se i "Skapa objekt".

Det som gör klasspaket specifika är piloperatören -> . Det kan användas efter ett blott ord:

Point->new(...);

eller efter en skalärvariabel (vanligtvis med en referens):

my @polar = $point->polar_coordinates;

Det som är till vänster om pilen förhindras till den angivna argumentlistan för metoden. Till exempel efter samtal

Point->new(1, 2);

array @_ i new kommer att innehålla tre argument: ('Point', 1, 2) .

Paket som representerar klasser bör ta hänsyn till denna konvention och förvänta sig att alla deras metoder kommer att ha ett extra argument.

Arv och metodupplösning

För att göra en klass till en underklass av en annan klass använder du 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 möjliggör flera arv:

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

Arv handlar om upplösning vilken metod som ska kallas i en viss situation. Eftersom ren Perl inte föreskriver några regler om den datastruktur som används för att lagra objektdata har arv ingenting att göra med det.

Tänk på följande klasshierarki:

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;

Metodupplösningen fungerar enligt följande:

  1. Startpunkten definieras av piloperatörens vänstra operand.

    • Om det är ett blott ord:

      Point2D->new(...);
      

      ... eller en skalärvariabel med en sträng:

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

      ... då är utgångspunkten paketet med motsvarande namn ( Point2D i båda exemplen).

    • Om den vänstra operanden är en skalvariabel med en välsignad referens:

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

      då är utgångspunkten klassens referens (igen, Point2D ). Piloperatören kan inte användas för att ringa metoder för oblankade referenser.

  2. Om utgångspunkten innehåller den nödvändiga metoden, kallas den helt enkelt.

    Point2D::new existerar,

    Point2D->new(...);
    

    kommer helt enkelt att kalla det.

  3. Om startpunkten inte innehåller den erforderliga metoden utförs djup-först-sökning i parent klasser. I exemplet ovan kommer sökordningen att vara följande:

    • Point2D
    • Point (första förälder till Point2D )
    • GeometryObject (moder till Point )
    • PlanarObject (andra förälder till Point2D )

    Till exempel i följande kod:

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

    metoden som kommer att kallas är GeometryObject::transpose , även om den skulle åsidosättas i PlanarObject::transpose .

  4. Du kan ange utgångspunkten uttryckligen.

    I föregående exempel kan du uttryckligen ringa PlanarObject::transpose så:

    my $point = Point2D->new(...);
    $point->PlanarObject::transpose(...);
    
  5. På liknande sätt utför SUPER:: metodsökning i överordnade klasser i den aktuella klassen.

    Till exempel,

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

    kommer att ringa Point::new under loppet av Point2D::new exekvering.

Klass- och objektmetoder

I Perl är skillnaden mellan klass (statisk) och objekt (instans) -metoder inte så stark som på vissa andra språk, men de finns fortfarande.

Den vänstra operanden för piloperatören -> blir det första argumentet för metoden som ska kallas. Det kan vara antingen en sträng:

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

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

eller en objektreferens:

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

Klassmetoder är bara de som förväntar sig att deras första argument ska vara en sträng, och objektmetoder är de som förväntar sig att deras första argument är en objektreferens.

Klassmetoder gör vanligtvis ingenting med sitt första argument, som bara är klassens namn. I allmänhet används det bara av Perl själv för metodupplösning. Därför kan en typisk klassmetod också kallas för ett objekt:

my $width = Point->canvas_width;

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

Även om denna syntax är tillåten är den ofta vilseledande, så det är bättre att undvika det.

Objektmetoder får en objektreferens som det första argumentet, så att de kan adressera objektdata (till skillnad från klassmetoder):

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;

Samma metod kan spåra båda fallen: när det kallas som en klass eller en objektmetod:

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

Definiera klasser i moderna Perl

Även om det finns tillgängligt rekommenderas inte att definiera en klass från början i moderna Perl. Använd ett av hjälp-OO-system som ger fler funktioner och bekvämlighet. Bland dessa system är:

Älg

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
}

Klass :: Accessor (Moose syntax)

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)

Klass :: Accessor (inbyggd syntax)

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

Klass :: Tiny

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

roller

En roll i Perl är i huvudsak

  • en uppsättning metoder och attribut som
  • injiceras direkt i en klass.

En roll tillhandahåller en bit av funktionalitet som kan komponeras i (eller tillämpas på) varje klass (som sägs konsumera rollen). En roll kan inte ärvas men kan konsumeras av en annan roll.

En roll kan också kräva konsumtionsklasser för att implementera vissa metoder istället för att implementera själva metoderna (precis som gränssnitt i Java eller C #).

Perl har inte inbyggt stöd för roller men det finns CPAN-klasser som ger sådant stöd.

Moose :: Roll

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

Roll :: Tiny

Använd om ditt OO-system inte ger stöd för roller (t.ex. Class::Accessor eller Class::Tiny ). Stödjer inte attribut.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow