Anforderung: Ein Hash-Objekt besitzt ein Attribut theAttribute, das
von einer gleichnamigen Attributmethode gekapselt wird. Das Attribut
erhält bei der Objekt-Instanziierung keinen Wert. Der Wert wird
stattdessen beim ersten Zugriff berechnet. Alle weiteren Zugriffe
liefern den berechneten Wert.
Ein naheliegender Ansatz, dies in Perl zu implementieren, ist:
1 |
sub theAttribute { |
2 |
my $self = shift; |
3 |
|
4 |
if (!defined $self->{'theAttribute'}) { |
5 |
... |
6 |
$self->{'theAttribute'} = $val; |
7 |
} |
8 |
|
9 |
return $self->{'theAttribute'}; |
10 |
} |
Hierbei ist $val der in Abschnitt ... berechnete Attributwert
(der auf dem Attribut gecached wird).
Diese Lösung ist relativ hässlich, da der Ausdruck
gleich drei Mal auftaucht.
Zum Glück lässt sich die Sache auch eleganter formulieren:
1 |
sub theAttribute { |
2 |
my $self = shift; |
3 |
|
4 |
return $self->{'theAttribute'} //= do { |
5 |
... |
6 |
$val; |
7 |
}; |
8 |
} |
Erklärung: Ist der Wert von theAttribute definiert, wird er
unmittelbar geliefert. Ist er nicht definiert, wird der do-Block
ausgeführt. Dessen Wert wird erst an das Attribut zugewiesen (=)
und dann von der Methode geliefert (return). Den
Defined-Or-Operator // gibt es seit Perl 5.10.
Ist die Objektstruktur komplexer als ein Hash, lässt sich die
Semantik von
$self->{$key} //= do { ... };
auch als Objektmethode mit einer anonymen Subroutine als Parameter
realisieren:
1 |
sub memoize { |
2 |
my ($self,$key,$sub) = @_; |
3 |
|
4 |
my $val = $self->get($key); |
5 |
if (!defined $val) { |
6 |
$val = $self->$sub($key); |
7 |
$self->set($key=>$val); |
8 |
} |
9 |
|
10 |
return $val; |
11 |
} |
Hierbei sind get() und set() die Methoden zum Abfragen und
Setzen des Attributwerts. Natürlich kann der Zugriff auf das Attribut
- in Abhängigkeit von der Klasse - auch anders realisert sein.
Angewendet auf obiges Beispiel:
1 |
sub theAttribute { |
2 |
my $self = shift; |
3 |
|
4 |
return $self->memoize('theAttribute',sub { |
5 |
... |
6 |
$val; |
7 |
}); |
8 |
} |
Links