Thread Role VS. Mixin?: Die Rolle des Elchs (4 answers)
Opened by Ronnie at 2007-04-20 01:22

Ronnie
 2007-04-21 20:16
#76065 #76065
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
Okay, hier mal ein Beispiel für die Möglichkeiten, die wir mit Perl6 und Roles bzw. Moose und Roles bekommen. Roles erlauben uns Methoden an Klassen zu binden, die bestimmte Voraussetzungen erfüllen. Man könnte Vergleichbares durch Mehrfachvererbung erreichen, aber Roles sind eleganter, da sie völlig unterschiedliche Klassen um eine bestimmte Funktionalität erweitern.

Hier ein Beispiel für eine Role:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/perl

use strict;
use warnings;

package RoleDigraph;
use Moose::Role;

requires '_as_queque';

sub as_digraph {
my $self = shift;
my @queque = @{$self->_as_queque};
my $o = 'digraph G {' . "\n";
$o .= "\t" . $_->[0] . ' -> ' . $_->[1] . ";\n" for @queque;
$o .= "}\n";
return $o;
}

1;

Wie man sieht haben wir nur eine Methode, keine Instanzvariablen etc. Die Role benötigt eine Methode _as_queque() die von der Klasse angeboten werden muss, die die Role einbinden will. Diese Klasse wird dann um die Methode as_digraph() erweitert, die aus einer Liste von Tupeln (als Queque bezeichnet) einen Digraph erzeugt, wie er mit http://www.graphviz.org/ dot visualisiert werden kann.

Hier jetzt zwei Beispiele in denen die Role in die Klasse eingebunden wird, um deren Funktionalität zu erweitern. Das Baum-Beispiel (aus einem relativ aktuellen Thread im Forum) ist nur bedingt geeignet, weil der Baum als Graph nur teilweise richtig visualisiert werden kann:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

package BinTree;
use Moose;
with 'RoleDigraph';

use overload '""' => \&to_s;

has 'val' => ( is => 'rw', isa => 'Num' );
has 'l' => ( is => 'rw');
has 'r' => ( is => 'rw');

sub _set_l {
my $self = shift;
my $val = shift;

if (blessed $self->l) { $self->l->append($val); }
else { $self->l($self->new(val => $val)); }
}

sub _set_r {
my $self = shift;
my $val = shift;

if (blessed $self->r) { $self->r->append($val); }
else { $self->r($self->new(val => $val)); }
}

sub to_s {
my $self = shift;
my $v = $self->val;
my $l = blessed $self->l ? $self->l->to_s : undef;
my $r = blessed $self->r ? $self->r->to_s : undef;
return join ', ', grep defined, ($l, $v, $r);
}

sub append {
my $self = shift;
my @vals = @_;
while ( my $n = shift @vals ) {
if ($n < $self->val) { $self->_set_l($n) }
elsif ($n > $self->val) { $self->_set_r($n) }
}
}

sub _as_queque {
my $self = shift;
my $queque = shift || [];

my $v = $self->val;

if (blessed $self->l) {
push @$queque, [$v, $self->l->val];
$self->l->_as_queque($queque);
}

if (blessed $self->r) {
push @$queque, [$v, $self->r->val];
$self->r->_as_queque($queque);
}

return $queque,
}

package main;

my @list = (20, 4, 28, 9, 100, 12, 84, 10, 11);
my $tree = BinTree->new( val => shift @list );
$tree->append(@list);
$tree->append(1, 50, 200);

print $tree . "\n" x 2;
print $tree->as_digraph;

http://www.ronnie-neumann.de/trashbin/tree.png

In dem zweiten Beispiel wird ein einfaches Soziogramm aus einem Logfile erstellt:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

package Event;
use Moose;

has 'source' => ( is => 'rw', isa => 'Str' );
has 'destination' => ( is => 'rw', isa => 'Str' );
has 'id' => ( is => 'rw', isa => 'Num' );

package LogAnalyzer;
use Moose;
with 'RoleDigraph';

has 'events' => ( is => 'rw', isa => 'ArrayRef' );

sub parse_line {
my $self = shift;
my $line = shift;
chomp($line);

my ($s) = $line =~ m/source:(\w+)\s*/; # we ignore the domains
my ($d) = $line =~ m/destination:(\w+)\s*/;
my ($id) = $line =~ m/id:(\w+)\s*/;

my $event = Event->new(
source => $s,
destination => $d,
id => $id
);

push @{$self->{events}}, $event;
}

sub _as_queque {
my $self = shift;
my $queque = [];

@$queque = map { [$_->source, $_->destination] } @{$self->events};

return $queque;
}

package main;

my $al = LogAnalyzer->new;
$al->parse_line($_) while (<DATA>);
print $al->as_digraph;

__DATA__
source:joe@example.com destination:bob@example.com id:685421
source:bob@example.com destination:tim@example.com id:685424
source:joe@example.com destination:tina@example.com id:685429
source:tim@example.com destination:bob@example.com id:685431
source:tina@example.com destination:bob@example.com id:685441
source:joe@example.com destination:bob@example.com id:685452

http://www.ronnie-neumann.de/trashbin/sozio.png

Wie man sieht, werden beide Klassen elegant um ein und dieselbe Methode erweitert, auch wenn es völlig unterschiedliche Klassen und Anwendungen sind.\n\n

<!--EDIT|Ronnie|1177172450-->

View full thread Role VS. Mixin?: Die Rolle des Elchs