Moose: Eine (post-)moderne OOP-Erweiterung für Perl

Seite 4: Vererbung

Inhaltsverzeichnis

Mitunter haben vererbte Attribute ungewollte Eigenschaften, die sich mit einem + vor den Namen in der erbenden Klasse jedoch überschreiben lassen:

   has '+farbe' => {
lazy => 0,
builder => 'farbgenerator',
}

In der neues Raumschiffklasse verhält sich farbe wie bekannt, nur wird $ufo->farbgenerator anstatt $ufo->_build_farbe; schon während der Objekterzeugung aufgerufen.

Erwartungsgemäß lassen sich auch die erhaltenen Methoden überschreiben, sogar mit Routinen anderen Namens:

   override 'stop' => \&passagierfreundlicher_halt;

Die abgeleitete Klasse kennt somit keine Methode stopsondern etwas wesentlich komfortableres. Oft genügt es auch schon, nur ein wenig Funktionalität hinzuzufügen:

   before 'stop' => sub {
shift->bordfunk('Bitte schnallen sie sich an!');
};

und vielleicht sogar:

   after 'stop' => sub { shift->standlicht(1); };

Diese beiden Erweiterungen kann man nun zusammenfassen:

   around 'stop' => sub {
my $original = shift;
my $self = shift;
$self->bordfunk('Bitte schnallen sie sich an!');
super();
$self->standlicht(1);
}

super() ruf die Orginalmethode auf, mit den gleichen Argumenten. Wer mehr Kontrolle benötigt, greift zu $self->SUPER::stop() oder $self->$original(). Auf diese Art lassen sich Methoden beliebig oft "einwickeln" (der Perl-6-Befehl dafür heisst wrap). Der umgekehrte Weg steht Entwicklern ebenfalls offen. In der Variante schreibt man in die Elternklasse:

   sub stop {
$self->bordfunk('Bitte schnallen sie sich an!');
inner();
$self->standlicht(1);
}

Die Erben können dann entscheiden, ob sie hart oder sanft bremsen wollen, indem sie stop
individuell implementieren. Bordfunk und Standlicht wird davon unabhängig auf jeden Fall betätigt, wobei Erben die Ausführung mit inner() beliebig weiterreichen können:

   augment 'stop' => sub {
my $self = shift;
my $a = shift; # Beschleunigung
$self->geschwindigkeit( $self->geschwindigkeit - $a)
while $self->geschwindigkeit > 0;
$self->geschwindigkeit(0);
};

Da die Beschleunigung nur numerisch sein darf, bietet sich auch hier eine Typenprüfung an. Der Autor empfiehlt den Einsatz von MooseX::Method::Signatures statt MooseX::Params::Validate. Letzteres wirkt ein wenig umständlich und weniger leistungsfähig. Zudem wird MooseX::Method::Signatures zusammen mit dem empfohlenen MooseX::Declare geladen. Es nähert die Syntax noch einmal ein gutes Stück an Perl 6 an und spart das notorische my $self = shift;

   method stop (Num :$a! = 1, DateTime $wann? where {$_ >= DateTime->now}) { ...

Der Doppelpunkt markiert benannte Parameter. Durch das Ausrufezeichen wird eine Anwesenheit eines Argumentes erzwungen. Per Default sind benannte Parameter optional, positionale (ohne Doppelpunkt) sind es nicht. Auf diesem Weg lassen sich selbst Subtypen und Default-Werte gleich in der Signatur ausweisen. Sie funktionieren auch noch nach der Vererbung, selbst wenn der Erbe kein MooseX::Method::Signatures einsetzt.

Moose-Objekte erben mit extends:

   class UFO extends Excalibur { ...

Der Befehl extends steht normalerweise in der Klasse, sofern nicht das angetragene MooseX::Declare zum Einsatz kommt. Mit der beliebten Erweiterung darf extends auch in der Klassendefinition stehen. Eine Mehrfachvererbung erfolgt immer innerhalb der Klasse nach dem Muster:

   extends 'Excalibur', 'White Star';

Ein erneuter Aufruf von extends würde diese Definition überschreiben. Zur Vererbung und weiteren Moose-Themen ließe sich noch weit mehr sagen, was den Artikel zu epischer Länge dehnen würde. Ein zweiter Artikel wird deshalb das interessante Konzept der Rollen (Roles) vorstellen, das über Moose in die Perl-5-Welt Einzug hielt.

Herbert Breunung
schreibt regelmäßig Artikel über Applikationsentwicklung in Perl und Perl 6 und spricht auf Konferenzen im In- und Ausland. Er führt seit Jahren ein freies Softwareprojekt und ist am Aufbau der Perl-6-Dokumentation beteiligt.
(rl)