Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]8353[/thread]

Perl OOP



<< |< 1 2 >| >> 13 Einträge, 2 Seiten
sid burn
 2006-09-21 19:24
#70139 #70139
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Hi,
ich wollte etwas machen, eine ungefähre Lösung habe ich zwar schon. Allerdings weiß ich nicht ob dies die beste Lösung ist, oder ob es nicht bessere Möglichkeiten gibt.

Hier einmal eine kurz beschrieben was ich machen wollte:
Ich habe ein Benutzer der Unterschiedliche Attribute besitzt. "Username", "Password", "email", "duration", ...

Zum setzen und Auslesen der Daten habe ich mich entschieden Setter und getter zusammen zu schreiben.

Code: (dl )
$user->user

gibt mir also den benutzernamen aus
Code: (dl )
$user->user('anne')

würde den benutzernamen setzen. Sowas ist ja eigentlich einfach und klappt soweit. Nun wollte ich das ganze aber erweitern.

Ich möchte das bestimmte Methoden nur für bestimmte Attribute verfügbar sind z.B. wenn man die Duration um 1 Tag erhöhen möchte sollte man einfach ein zusätzlich add hinzufügen. Dieses "add" sollte es aber nur für dieses Attribut geben, andere Attribute sollten Ihre eigene "add" Methode Implementieren können.

Jetzt möchte ich aber ungern ein "add_duration", "add_foo" etc. hinzufügen.

Irgendwie dachte ich an eine ähnliche Funktionalität wie so etwas.
Code: (dl )
$user->duration->add(1)


Das obere habe ich schon in etwa hinbekommen. Dabei gehe ich nun folgendermaßen vor das ich "user" "user::name", "user::password" etc. als Klasse besitze.

Innerhalb der "user" Klasse baue ich ein Hash auf. Der Schlüssel ist der Attributname und der Value ist eine Objektreferenz auf "user::name", "user::password" etc.

In meiner "user" Klasse habe ich dann z.B. eine Methode "password" erzeugt die mir Wiederrum aus dem hash das entsprechende "user::password" Object zurück gibt.

Somit bin ich in der Lage "$user->password->get" zu schreiben und alles Funktioniert.


Jetzt zu meinen Anmerkungen:
1) Leider bin ich jetzt nicht mehr in der Lage einfach nur "$user->password" und "$user->password('neues_password')" zu schreiben. Da ja die Methode password eine Objektreferenz zurück gibt. Gibt es irgendeine Möglichkeit das trotzdem zu Implementiere?

Praktisch wäre wenn nichts weiteres kommt einfach die get Methode aufgerufen wird, und wenn doch ein Attribut angegeben wird, die set Methode aufgerufen wird. Allerdings kann man nicht innerhalb der Methode überprüfen ob die Methode wiederrum als Objekt aufgerufen wird?

2) Es sieht mir ziemlich unpraktisch aus. Man schreibt eigentlich für solch eine kleine Implemenatation ziemlich viel Code. Zum anderen enthalten "user::password" etc. immer nur ein einzigen Attributwert. Das schaut mir irgendwie nach Verschwendung von Klassen aus, wenn ich letztendlich aus jeden Attributwert nochmals eine Klasse mache.


Gibt es da irgendwie eine einfachere, oder bessere Möglichkeiten solch eine Funktionalität zu Implementieren? Oder ein ganz anderen Weg dies zu machen?


Mein bisheriger Code mit dem ich das ganze getestet habe schaut folgendermaßen aus:
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
#!/usr/bin/perl
use warnings;
use strict;
use constant TRUE => 1;
use constant FALSE => 0;
use FindBin qw($Bin);

{ package user::name;
sub new {
my $class = shift;
my $name = shift;
bless \$name, $class;
}
sub get {
my $self = shift;
return $$self;
}
sub set {
my $self = shift;
$$self = shift;
}
}

{ package user::password;
sub new {
my $class = shift;
my $password = shift;
bless \$password, $class;
}
sub get {
my $self = shift;
return $$self;
}
sub set {
my $self = shift;
$$self = shift;
}
}

{ package user;
sub new {
my $self = bless {}, shift;
my %options = @_;
$self->{name} = user::name->new ( $options{name} );
$self->{password} = user::password->new( $options{password} );
return $self;
}
sub name {
my $self = shift;
$self->{name};
}
sub password {
my $self = shift;
$self->{password};
}
}

my $paul = user->new(
name => 'paul',
password => 'passwd' ,
);

my $anne = user->new(
name => 'anne',
password => 'nopwd',
);


print $paul->name->get, "\n", $paul->password->get, "\n";
print "\n";
print $anne->name->get, "\n", $anne->password->get, "\n";

Ausgabe:
Code: (dl )
1
2
3
4
5
paul
passwd

anne
nopwd
\n\n

<!--EDIT|sid burn|1158852730-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
betterworld
 2006-09-21 20:03
#70140 #70140
User since
2003-08-21
2614 Artikel
ModeratorIn

user image
[quote=sid burn,21.09.2006, 17:24]1) Leider bin ich jetzt nicht mehr in der Lage einfach nur "$user->password" und "$user->password('neues_password')" zu schreiben. Da ja die Methode password eine Objektreferenz zurück gibt. Gibt es irgendeine Möglichkeit das trotzdem zu Implementiere?[/quote]
Das zweite ($user->password('neues_password')) koenntest Du ja machen, indem Du if (@_) { ... } schreibst.

Beim ersten koenntest Du vielleicht den Stringification-Operator ueberladen (siehe perldoc overload). In dem Fall wuerde aber der Speicher fuer den ganzen Hash trotzdem belegt werden.

Du kannst Dir ja auch mal Class::Accessor und aehnliche angucken, vielleicht kannst Du ein paar Tricks abgucken.\n\n

<!--EDIT|betterworld|1158854674-->
sid burn
 2006-09-21 20:38
#70141 #70141
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Danke dir.

War ja letztendlich einfacher als erwartet. Ich dachte schon das wäre mit Perl nicht möglich.

Ich habe es jetzt so gemacht und in "user::name" und "user::password" einfach ein "use overload '""' => \&get" hinzugefügt. Damit konnte ich einfach "$anne->name" schreiben und habe den benutzer string zurück bekommen.

In der Klasse "user" habe ich dann die Methode "name", "password" angepasst wie du es gesagt hast. Wenn also noch etwas in @_ steht wird einfach "$self->{name}->set(@_)" aufgerufen.

Jetzt läuft das so wie erwartet. ich finde das ist aber immer noch eine Menge was man da Schreiben muss. Denke aber da wird man bestimmt was mit eval() Automatisieren können. So das die einzelnen Klassen und get/set Methoden automatisch angelegt werden.

Dein genanntes Modul werde ich mir auch nochmal anschauen. :)\n\n

<!--EDIT|sid burn|1158856791-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
pq
 2006-09-22 11:15
#70142 #70142
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
also mir kommt dein aufwand übertrieben vor.
du möchtest dir die arbeit sparen, add_* methoden zu schreiben, musst
aber letztendlich doch für jede attribut-klasse so eine methode schreiben,
und jetzt sagst du, du möchtest dir sparen, die accessor-methoden
zu schreiben. da hätte ich zuerst angefagen zu sparen, und da
gibt es auch schon lange module für, die dir das abnehmen (eben z.B.
CPAN:Class::Accessor). und wenn du es selbst machst,
solltest du auch nicht eval nehmen (ich vermute, du meintest
string-eval), sondern das ist in der regel für sowas unnötig. methoden
kannst du einfach mit *$subname = sub { my ($self) ... }
anlegen.
wie gesagt, dein ansatz kommt mir sehr kompliziert vor, und ich habe
auch noch nicht begriffen, was du dir dadurch letzendlich sparst.

zudem finde ich, dass ein attribut nur dann selbst ein objekt sein
sollte, wenn es mehr als nureine zahl oder ein string ist. eben
wenn es selbst attribute hat. aus einfachen zahlen-attributen objekte zu
machen, hat auch noch den nachteil, dass es alles langsamer macht.
ein einziger zugriff muss nun plötzlich 2 methoden aufrufen statt einer.\n\n

<!--EDIT|pq|1158909536-->
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
sid burn
 2006-09-22 12:52
#70143 #70143
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Quote
also mir kommt dein aufwand übertrieben vor.

Mir auch. ;)
Quote
du möchtest dir die arbeit sparen, add_* methoden zu schreiben, musst
aber letztendlich doch für jede attribut-klasse so eine methode schreiben,
und jetzt sagst du, du möchtest dir sparen, die accessor-methoden
zu schreiben. da hätte ich zuerst angefagen zu sparen, und da
gibt es auch schon lange module für, die dir das abnehmen (eben z.B.
CPAN:Class::Accessor). und wenn du es selbst machst,
solltest du auch nicht eval nehmen (ich vermute, du meintest
string-eval), sondern das ist in der regel für sowas unnötig. methoden
kannst du einfach mit *$subname = sub { my ($self) ... }
anlegen.

Das habe ich so schon Implementiert. Also die Methoden über Typeglobs angelegt anstatt String eval. Mit eval() meinte ich eher Generell eine Möglichkeit der Automatischen Code Generierung.

Quote
wie gesagt, dein ansatz kommt mir sehr kompliziert vor, und ich habe auch noch nicht begriffen, was du dir dadurch letzendlich sparst.

Sparen tue ich dadurch nichts. Ich habe meine Unterschiedlichen Attribute. Als erstes werden diese Attribute nicht direkt abgespeichert, sondern später aus einer Datenbank ausgelesen. Dafür möchte ich eine Klasse schreiben um das anlegen und das ändern der Werte über eine Klasse zu machen.

Zum anderen kommt es z.B. vor das ich das Attribut "duration" habe, das die zeit enthält wann der benutzer abläuft. In der Datenbank steht natürlich ein Datum. Um jetzt ein Tag hinzuzufügen möchte ich "$user->duration->add(1)" z.B. schreiben. Den 1 Tag drauf zu rechnen macht dann die Methode.

Zum anderen Möchte ich bei normaler Verwendung die duration Lesen/Schreiben wie sonst auch, also so als wenn es eben keine Klasse wäre. Zum anderen wäre es blöd wenn "name" als Attribut implementiert ist also "$user->name" zum setzen und lesen funktioniert. Und ich bei "duration" ständig "duration->get" oder "duration->set" schreibe. Das wirkt irgendwie nicht mehr intuitiv und konsistent.

Allerdings sollte "add" ja nicht mit "user" oder sonstigen Attributen Funktionieren, sondern nur mit "duration". Zum anderen sieht es aber unnatürlich aus wenn ich "$user->add(1)" schreibe. Hier fehlt irgendwie der Bezug zu "duration". Und "duration_add" finde ich unpraktisch.

Zum anderen finde ich gehört die add Methode halt zu Duration, und nicht in eine allgemeine Klasse.

Quote
zudem finde ich, dass ein attribut nur dann selbst ein objekt sein
sollte, wenn es mehr als nureine zahl oder ein string ist. eben
wenn es selbst attribute hat. aus einfachen zahlen-attributen objekte zu
machen, hat auch noch den nachteil, dass es alles langsamer macht.
ein einziger zugriff muss nun plötzlich 2 methoden aufrufen statt einer.

Genau das meinte ich mit "Verschwendung von Klassen". Da teile ich genauer deine Meinung. Deswegen noch meine Frage wie ich es anders machen könnte. Class::Accessor muss ich mir nochmal anschauen, denke aber letztendlich das es letztendlich das selbe macht was ich nachbaue.

Bisher sieht mein Code so aus:
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
{ package user;
{ #start lexical scope
my @class_attributes = qw/ name password /;

no strict 'refs';
for my $attribute ( @class_attributes ) {
# Create new Package with Overload
eval 'package '.__PACKAGE__.'::'.$attribute.';'.'use overload \'""\' => \&get;';
# Create method new for new package
*{__PACKAGE__.'::'.$attribute.'::'.'new'} = sub {
die "Not enough Paramters" if @_ < 2;
bless \$_[1], $_[0];
};
# Create method set/get for new package
*{__PACKAGE__.'::'.$attribute.'::'.'get'} = sub { return ${$_[0]} };
*{__PACKAGE__.'::'.$attribute.'::'.'set'} = sub { ${$_[0]} = $_[1] };

# Create Methods in this package to Call the Classes
*{$attribute} = sub {
my $self = shift;
if (@_) { $self->{$attribute}->set(@_) }
else { $self->{$attribute} }
};
}
} #end lexical scope

sub new {
my $self = bless {}, shift;
my %options = @_;
$self->{name} = user::name->new ( $options{name} );
$self->{password} = user::password->new( $options{password} );
return $self;
}
}

my $paul = user->new;
$paul->name("Paulchen");
$paul->password("123456");

my $anne = new user;
$anne->name("anne");
$anne->password("654321");

print $paul->name, "\n", $paul->password, "\n";
print "\n";
print $anne->name, "\n", $anne->password, "\n";


Bisher klappt das ganze wenn ich alles in einer Datei packe, versuche ich aber das package "user" in einem Modul zu packen klappt es nicht mehr. Ich denke es gibt da anscheind Probleme mit Laufzeit/Runtime Kompilation.

EDIT:
Das mit dem Modul klappt doch, und es gab keine Probleme mit der Laufzeit/Kompilierzeit. Hatte nur etwas zusätzliches implementiert, und die Hälfte dabei vergessen. :blush:\n\n

<!--EDIT|sid burn|1158917164-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
pq
 2006-09-22 14:20
#70144 #70144
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
ich finde deinen ansatz immer noch zu kompliziert und du hast immer
noch nicht gesaagt, was du gewinnst. und ich habe auch nicht verstanden,
was an einer methode add_duration jetzt so schlecht ist. ich finde
z.B. duration->add viel schlechter. wenn dann inc, denn duration ist
ja nur eine zahl. dass du was hinzufügst, gehört IMHO von der logik her
in die ursprüngliche klasse.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
sid burn
 2006-09-22 15:28
#70145 #70145
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Quote
ich finde deinen ansatz immer noch zu kompliziert und du hast immer noch nicht gesaagt, was du gewinnst.

Wenn ich die duration um ein Tag erhöhen möchte, dann möchte ich dies lediglich so schreiben
"$user->duration->add(1)" und nicht so "$user->duration_add(1)"

Letztendlich ist es das gleiche wie in anderen programmiersprachen, wo ich einfach nochmals hinten dran einfach eine methode aufrufen kann. Und es gibt halt ein paar Funktionen die es nur für bestimmte Attribute gibt. z.B. wird das Passwort durch ein Hashwert ersetzt, beim Anlegen soll aber noch das "plaintext" Passwort zur Verfügung stehen.

Daher möchte ich "$user->password" das Passwort erzeugen lassen und auslesen, wenn ich das plain Passwort möchte, möchte ich nur "$user->password->plain" schreiben um es auszulesen. Und halt eben nicht "$user->password_plain"

Klar könnte ich das auch machen. Man könnte auch "net_ftp_connect" schreiben anstatt extra ein namespace zu benutzen. Und dann wieder zurück richtung C gehen oder so wie es PHP macht.

Ich finde diese Möglichkeit also ganz gut, das die Attribute eine reihe von eigenen Methoden besitzen. Und genau das wollte ich einfach mal Implementieren.

Quote
duration->add viel schlechter. wenn dann inc, denn duration ist ja nur eine zahl. dass du was hinzufügst, gehört IMHO von der logik her in die ursprüngliche klasse.

Wenn ich 1 Tag hinzuaddieren. Warum sollte dies zu User gehören. Warum sollte diese Methode auch für "name", "password", "email" zur Verfügung stehen? Diese Methode hat doch gar nichts mit den anderen Attributen zu tun. Und ist speziell eine Methode nur für dieses Attribut.

Von daher ist es meiner meinung nach unter "duration" oder wo es sonst gebraucht wird, besser aufgehoben.

Das man letztendlich aber eine Klasse hat die nur ein Attribut beinhaltet finde ich ebenso unschön. Von mir aus muss es ja auch nicht so sein das ich wiederrum Klassen benutze. Ich würde auch eine andere Weg benutzen. Ein anderer Fällt mir aber nicht ein.

Deswegen auch zum einen dieser Thread hier, ob es noch andere Möglichkeiten gibt.\n\n

<!--EDIT|sid burn|1158924827-->
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
renee
 2006-09-22 16:38
#70146 #70146
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Also Dein oben gepostetes User-Beispiel kann man auch einfach so schreiben:

User.pm
Code: (dl )
1
2
3
4
5
6
7
8
9
package User;

use strict;
use warnings;
use base 'Class::Accessor';

User->mk_accessors(qw/name passwort/);

1;


Dann im Skript:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/perl

use strict;
use warnings;
use User;

my $user = User->new();
$user->name('test');

print $user->name();
\n\n

<!--EDIT|renee|1158928740-->
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
pq
 2006-09-22 17:24
#70147 #70147
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
[quote=sid burn,22.09.2006, 13:28]
Wenn ich die duration um ein Tag erhöhen möchte, dann möchte ich dies lediglich so schreiben
"$user->duration->add(1)" und nicht so "$user->duration_add(1)"
[/quote]
schon klar, aber warum?
Quote
Letztendlich ist es das gleiche wie in anderen programmiersprachen, wo ich einfach nochmals hinten dran einfach eine methode aufrufen kann.

ich weiß nicht, bei simplen integern oder strings rufst du doch in
anderen sprachen auch keine methode mehr auf.
Quote
Klar könnte ich das auch machen. Man könnte auch "net_ftp_connect" schreiben anstatt extra ein namespace zu benutzen. Und dann wieder zurück richtung C gehen oder so wie es PHP macht.

moment, moment. du hast mich glaube ich nicht verstanden.
net_ftp_connect ist ja witzlos. wie gesagt, simple integer oder strings
finde ich als objekte overkill. und da, wo du meinst, ein objekt haben
zu müssen, mach auch eins. aber nicht implizit bei *jedem* attribut.
Quote
Wenn ich 1 Tag hinzuaddieren. Warum sollte dies zu User gehören. Warum sollte diese Methode auch für "name", "password", "email" zur Verfügung stehen? Diese Methode hat doch gar nichts mit den anderen Attributen zu tun. Und ist speziell eine Methode nur für dieses Attribut.

jetzt bin ich ganz verwirrt. wenn der user eine methode add_duration hat,
warum steht die plötzlich "für "name", "password", "email" zur Verfügung"?
und wenn ich die duration eines users erhöhen will (was auch immer
das bedeutet), dann gehört diese methode für mich schon zum user.
Quote
Das man letztendlich aber eine Klasse hat die nur ein Attribut beinhaltet finde ich ebenso unschön. Von mir aus muss es ja auch nicht so sein das ich wiederrum Klassen benutze. Ich würde auch eine andere Weg benutzen. Ein anderer Fällt mir aber nicht ein.

bis jetzt sehe ich nur, dass dir ->foo_bar nicht gefällt und du lieber
->foo->bar schreiben willst. das finde ich gut für attribute, die richtige
objekte sind. wenn du der meinung bist, duration verdient eine eigene
klasse, dann hält dich nichts davon ab, das zu machen.
ich finde einzig und allein den ansatz schlecht, generell jedesattribut
einer klasse so zu behandeln. dann kommst du vom hundertsten ins
tausendste. du verschwendest speicher und cpu.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
pq
 2006-09-22 17:31
#70148 #70148
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
[quote=renee,22.09.2006, 14:38]
Code: (dl )
1
2
3
4
5
6
7
8
9
package User;

use strict;
use warnings;
use base 'Class::Accessor';

User->mk_accessors(qw/name passwort/);

1;
[/quote]
und mit duration:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package User;

use strict;
use warnings;
use base 'Class::Accessor';
# User->follow_best_practice; # get_* und set_*
User->mk_accessors(qw/name passwort duration/);

sub add_duration {
 $_[0]->duration(
   $_[0]->duration() + $_[1]
 );
}
1;

finde ich verständlich und kurz. wobei ich set_ und get_ methoden
bevorzuge (geht mit User->follow_best_practice). das hat
u.a. den vorteil, dass man auch leicht readonly-klassen erzeugen kann
und man den unterschied sehr schnell sieht. naja, PBP hat mich da
überzeugt =)\n\n

<!--EDIT|pq|1158932718-->
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
<< |< 1 2 >| >> 13 Einträge, 2 Seiten



View all threads created 2006-09-21 19:24.