Kurz: In Perl ist eine Klasse ein Paket, ein Objekt eine gesegnete (bless) Referenz, eine Methode eine Subroutine, die das Objekt als erstes Argument bekommt:
Teil des Kurses Perl
Klassen in Perl
In Perl ist eine Klasse ein Paket, ein Objekt eine gesegnete (bless) Referenz, eine Methode eine Subroutine, die das Objekt als erstes Argument bekommt:
use strict; use warnings;
package Tier;
sub new {
my ($klasse, %args) = @_;
my $self = bless { name => $args{name} // "unbekannt", laute => 0 }, $klasse;
return $self;
}
sub name { $_[0]{name} }
sub laut { "..." }
sub vorstellen {
my $self = shift;
return sprintf("%s (%s) sagt %s", $self->name, ref($self), $self->laut);
}
package Hund;
our @ISA = ('Tier'); # Vererbung
sub laut { "Wuff" }
sub apportiere { my ($self, $was) = @_; return $self->name . " holt $was" }
package Katze;
use parent -norequire, 'Tier'; # moderner: use parent
sub new {
my ($klasse, %args) = @_;
my $self = Tier::new($klasse, %args);
$self->{leben} = $args{leben} // 9;
return $self;
}
sub laut { "Miau" }
sub vorstellen {
my $self = shift;
return $self->SUPER::vorstellen() . " und hat $self->{leben} Leben";
}
package main;
my @tiere = (Hund->new(name => "Rex"), Katze->new(name => "Mimi"));
print $_->vorstellen, "\n" for @tiere;
print $tiere[0]->apportiere("Stock"), "\n";
print $tiere[1]->isa('Tier') ? "Katze ist ein Tier\n" : "";
print Hund->can('apportiere') ? "Hund kann apportieren\n" : "";
print Katze->can('apportiere') ? "" : "Katze kann nicht apportieren\n";
my $methode = "laut";
print $tiere[0]->$methode, "\n"; # dynamischer MethodenaufrufAusgabe:
Rex (Hund) sagt Wuff
Mimi (Katze) sagt Miau und hat 9 Leben
Rex holt Stock
Katze ist ein Tier
Hund kann apportieren
Katze kann nicht apportieren
WuffPerl verbirgt nichts: Das Objekt ist ein Hash, Eigenschaften stehen in $self->{...}. Kapselung ist Konvention (Felder mit _ als privat).
Accessoren, Destruktor, Overload
use strict; use warnings;
package Konto;
use overload
'""' => \&als_text,
'+' => \&plus,
'==' => sub { $_[0]->stand == $_[1]->stand },
'<=>' => sub { my ($a, $b, $swap) = @_; ($swap ? -1 : 1) * ($a->stand <=> (ref $b ? $b->stand : $b)) };
my $anzahl = 0;
sub new { my ($c, $inhaber, $stand) = @_; $anzahl++; bless { inhaber => $inhaber, stand => $stand // 0 }, $c }
sub stand { $_[0]{stand} }
sub inhaber { $_[0]{inhaber} }
sub einzahlen { my ($s, $b) = @_; die "Betrag muss positiv sein\n" if $b <= 0; $s->{stand} += $b; $s }
sub als_text { sprintf("%s: %.2f", $_[0]->inhaber, $_[0]->stand) }
sub plus { Konto->new($_[0]->inhaber, $_[0]->stand + (ref $_[1] ? $_[1]->stand : $_[1])) }
sub anzahl { $anzahl }
sub DESTROY { }
package main;
my $k = Konto->new("Mia", 100);
$k->einzahlen(50)->einzahlen(25);
print "$k\n";
my $summe = $k + 25;
print "$summe\n";
print "gleich\n" if $k == Konto->new("Tom", 175);
print "größer\n" if $k > Konto->new("X", 5);
eval { $k->einzahlen(-1) };
print "Fehler: $@";
print "Konten: ", Konto->anzahl, "\n";Ausgabe:
Mia: 175.00
Mia: 200.00
gleich
größer
Fehler: Betrag muss positiv sein
Konten: 4Moderne OOP: Moo und die Klasse-Syntax
In echten Projekten nutzt man Moo (oder Moose) für weniger Schreibarbeit. Ab Perl 5.38 gibt es zudem experimentell das Schlüsselwort class:
package Person;
use Moo;
use Types::Standard qw(Str Int);
has name => (is => 'ro', isa => Str, required => 1);
has alter => (is => 'rw', isa => Int, default => 0);
has hobbys => (is => 'ro', default => sub { [] });
sub vorstellen { my $s = shift; sprintf("%s (%d)", $s->name, $s->alter) }
package Student;
use Moo;
extends 'Person';
has uni => (is => 'ro');
around vorstellen => sub { my ($orig, $self) = @_; $self->$orig() . " @ " . $self->uni };use v5.38;
use experimental 'class';
class Punkt {
field $x :param = 0;
field $y :param = 0;
field @verlauf;
method verschiebe($dx, $dy) { push @verlauf, [$x, $y]; $x += $dx; $y += $dy; return $self }
method position { "($x, $y)" }
method schritte { scalar @verlauf }
}
my $p = Punkt->new(x => 1, y => 2);
$p->verschiebe(3, 4)->verschiebe(-1, 0);
say $p->position, " nach ", $p->schritte, " Schritten";Ausgabe:
(3, 6) nach 2 SchrittenTests mit Test::More
Perl hat eine lange Testkultur (TAP-Format). Tests liegen in t/*.t und laufen mit prove:
use strict; use warnings;
use Test::More tests => 7;
sub addiere { $_[0] + $_[1] }
is(addiere(2, 3), 5, "Addition");
isnt(addiere(1, 1), 3, "Ungleich");
ok(addiere(0, 0) == 0, "Null");
like("Hallo Welt", qr/Welt/, "Regex passt");
is_deeply([1, { a => 2 }], [1, { a => 2 }], "Struktur gleich");
can_ok("Test::More", "is");
eval { die "kaputt\n" };
is($@, "kaputt\n", "Fehler gefangen");Ausgabe:
1..7
ok 1 - Addition
ok 2 - Ungleich
ok 3 - Null
ok 4 - Regex passt
ok 5 - Struktur gleich
ok 6 - Test::More->can('is')
ok 7 - Fehler gefangenJede Zeile beginnt mit ok oder not ok. Das Programm prove -v t/ fasst die Ergebnisse zusammen.
Merke
- Klasse = Paket, Objekt =
bless-te Referenz, Methode = Sub mit$self - Vererbung:
use parent,@ISA,SUPER:: use overloaddefiniert Operatoren;can,isa,reffür Introspektion- Moo/Moose für komfortable OOP; Perl 5.38 bringt
class Test::Moremitok,is,like,is_deeply;proveführt Tests aus
Übungsaufgabe
Schreibe eine Klasse Stapel mit push, pop, groesse und teste sie mit Test::More.
Quiz zur Selbstkontrolle
Was macht bless?
- Verbindet eine Referenz mit einer Klasse (richtig)
- Löscht ein Objekt
- Kopiert einen Hash
- Lädt ein Modul
Wie ruft man die Methode der Elternklasse auf?
- $self->SUPER::methode() (richtig)
- parent.methode()
- super()
- $self->parent()
Wofür dient prove?
- Führt Perl-Tests aus und fasst sie zusammen (richtig)
- Installiert Module
- Formatiert Code
- Startet einen Server
Weiter im Kurs
Zurück: Module, CPAN und Pakete
Weiter: Referenz und Spickzettel
Alle Kapitel: Perl im Überblick