FOK!forum / Digital Corner / [Perl] OOP tutorial / case study: DataRow.pm
Farenjidinsdag 22 januari 2008 @ 21:45
Ik merk dat heel veel mensen op dit forum moeite hebben met Object Oriented Programming. Het wordt ook als iets heel ingewikkelds gezien, terwijl het (als je het eenmaal doorhebt) zo helder en vanzelfsprekend is, dat je niet meer weet waarom je het ooit niet begrepen hebt. Maar zo makkelijk is dat nou ook weer niet. Ik heb er zelf ook flink mee geworsteld in het begin. Misschien kan ik dat jullie besparen met een tutorial/case study in OO perl. Je leert er over object georienteerd programmeren en hebt aan het eind nog een handige bouwsteen voor je OO applicaties ook! Ik heb perl gekozen omdat dat mijn favoriete taal is, maar alle begrippen en zelfs het object dat we gaan maken zijn ook in andere talen toepasbaar/implementeerbaar. Ik zal proberen er geen heel perl specifieke dingen in te doen.

We gaan hier een object maken dat je in je eigen objectmodel als basis kan gebruiken, en je heel veel tijd kan gaan schelen. Hoe vaak heb je niet een "select * from table" gedaan, waarna je met "while ($row = mysql_fetch_row($result))" oid een array vulde, of de row naar het scherm printte? Hoe vaak heb je niet editformuliertjes gemaakt waar je records mee kan bewerken en toevoegen? Hoe vaak heb je niet gedacht: "verdomme, doe ik nu weer hetzelfde?" Ik heb dat heel vaak gedacht en ik ben als programmeur van nature lui dus heb ik een mooie abstracte implementatie gemaakt, object georienteerd.

Het eindresultaat is best cool geworden. Te cool om alleen voor mezelf te houden. De DataRow class is een heel goed voorbeeld van wat OOP voor je kan doen en hoe het werkt. Heel vaak doe je eigenlijk hetzelfde, met oppervlakkige verschillen. We maken nu een uniforme interface (een API, Application Programming Interface) om als een object met datarecords in databasetabellen om te gaan.

Het wordt geen absolute beginnerstutorial, in de zin: ik ga ervan uit dat je wel een beetje kan programmeren en weet hoe dingen als variabelen, conditionals, loops etc werken. Ik zal dingen wel zoveel mogelijk toelichten, met een klein beetje PHP kennis moet je dit wel kunnen volgen. Als je de dingen uit deze tutorial zelf wil uitproberen, heb je voorlopig alleen perl nodig (later halen we er enkele perl libraries bij om wat specifieke dingen te doen). Bij de meeste OS'en is perl standaard aanwezig, maar mocht je zo'n OS hebben waar het niet bij zit: op http://www.activestate.com/ kun je het downloaden.

Eerst wat voorbeelden van toepassingen van het eindproject.

Stel je hebt een mysql table "pages" vol gegevens, met de velden: ID, title, content, bgcolor. En je wil een CMS systeem maken om deze tabel te bewerken. Dat is heel makkelijk:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/perl;

use strict;
use DataRow;    # importeer de functies van DataRow.pm
# snel alle records uit een tabel in xml formaat naar het scherm printen:
my $pages = DataRow->select( from => 'pages' );   # doet de query 'select * from pages' en geeft de rows als arrayref terug
print "<pages>"
for my $page (@{$pages}) {   
   print $page->toXML;

print "</pages>";

my $user = DataRow->new( ID => 23, dbTable => 'users' ); 
my $usercopy = $user->createCopy;   # creeer een kopie van de user

print $user->getField( field => 'name' );   # print de naam van de user
$usercopy->setField(
   email   => 'email@domain.com',   #pas het email adres aan
   password   => 'secretpassword',   #en update wachtwoord
);
$usercopy->save;   # opslaan en het staat in de database, onder een nieuw ID!


Makkelijk he! Ook zonder iets van programmeren te weten kun je wel raden wat er hier ongeveer gebeurt. En er is nog veel meer mogelijk.

Hoe werkt dit nou? We beginnen bij het begin: bij het Model. Object georienteerd programmeren betekent dat je werkt met objecten, dus laten we dat object gaan definieren. Welke eigenschappen heeft het, hoe kan je die eigenschappen uitlezen en aanpassen, en wat kan je er allemaal nog meer mee? We gaan ons hier nog niet druk maken om userinterfaces, weergave of programmaflow, dat komt later. Nu eerst de basis. Dat doen we in een bestand, DataRow.pm. In Perl heet dat een package. Dat begint met de naam van het package. Verder bevat het op zijn minst hoe een nieuw object van de DataRow classe gemaakt kan worden. Dit heet de constructor functie, en die heet meestal new(). Met "DataRow->new()" maak je dus een nieuw DataRow object aan.

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
package DataRow;

use strict;   # dit zorgt er oa voor dat alle variabelen altijd gedefinieerd moeten worden

sub new {
   # het eerste argument van de functie is in perl altijd de naam van de aangeroepen class (in dit geval "DataRow")
   my $classname = shift;

   my $args = {   # de argumenten met default values
      ID            => 0, 
      fields         => {},      # een hashref met <naam> => <value> paren
      dbTable         => undef,   # de tabel in de database die bij dit object hoort
      @_,    # @_ is een array met de argumenten die meegegeven zijn
   };

   # $self is het eigenlijke object, wat in wezen niks meer is dan een associatieve arrayref (hashref in perl lingo)
   # we stoppen er gewoon de meegegeven argumenten in
   my $self = {   
      ID            => $args->{ID},
      fields         => $args->{fields},
      dbTable         => $args->{dbTable},
   };
   
   bless $self, $classname;   # we "blessen" de hashref met de classname "DataRow", het is nu officieel een DataRow object
   return $self;   # en we retourneren het zojuist gemaakte nieuwe object
}


1; # een package bestand moet in perl altijd met een "true" (1) eindigen



Wat we nu hebben bereikt is: we hebben nu een structuur voor ons object. Het heeft de properties "ID", "fields", en "dbTable". "fields" is een assoc.array waar de velden en hun values instaan. We kunnen nu dus al een representatie maken van een gewone tabel in een sql database. De enige vereiste is dat er een ID veld is, de rest is flexibel.

We maken om het te testen een apart scriptje aan, page1.pl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/perl;

use strict;
use DataRow;    # importeer de functies van DataRow.pm
use Data::Dumper;   # een handige en onmisbare utility om inhoud van variabelen te printen

my $page = DataRow->new(
   fields   => {
      title   => 'Titel van pagina 1',
      description   => 'blaat',
      content      => 'Lorem ipsum, quia dolor sit, amet, consectetur, adipisci velit.'
   },
   dbTable   => 'pages'
)

print Dumper $page;   # print de structuur en inhoud


Maak het bestand uitvoerbaar met "chmod +x page1.pl" en voer het uit met "./page1.pl"

Je ziet dat ik niet alle argumenten heb gespecificeerd. Ik heb het veld ID weggelaten, maar het veld zit toch in het object. Het wordt gevuld met de defaultwaarde 0 die we hebben gedefinieerd in de constructor functie.

We kunnen er nu nog weinig mee. Maar dit was les 1. Volgende keer gaan we kijken hoe we netjes eigenschappen van het object kunnen uitlezen en aanpassen mbv object methods. Daarna gaan we ook zorgen dat we rows uit een database kunnen halen en weer op kunnen slaan.

Vragen? Stel ze!
Wolfjedinsdag 22 januari 2008 @ 22:11
Is perl wel een echte object georienteerde taal? Het ziet er namelijk uit als wat geknutsel om een oo effect te krijgen. De naam 'package' doet vermoeden dat je dat woord ook kunt gebruiken om een aantal methodes te groeperen onder een gezamenlijke naam. En 'new' lijkt een doodgewone methode.

Kun je in perl dan ook subclasses maken?
Farenjidinsdag 22 januari 2008 @ 22:18
quote:
Op dinsdag 22 januari 2008 22:11 schreef Wolfje het volgende:
Is perl wel een echte object georienteerde taal? Het ziet er namelijk uit als wat geknutsel om een oo effect te krijgen. De naam 'package' doet vermoeden dat je dat woord ook kunt gebruiken om een aantal methodes te groeperen onder een gezamenlijke naam. En 'new' lijkt een doodgewone methode.

Kun je in perl dan ook subclasses maken?
De implementatie in perl is inderdaad anders dan in sommige andere talen, het is er pas later "ingeklust" maar is wel volledig en heel elegant (vind ik persoonlijk). Je kan er alles mee wat je moet kunnen, dus kun je ook subclasses maken waarvan je erg flexibel kan overerven, hier kom ik misschien later nog op terug in deze tutorial.

En new() is inderdaad een gewone functie. Je kan de constructor ook nieuw() of poep() noemen, daar ben je vrij in. In perl heb je heel veel vrijheid. En ja, je kan een package ook gebruiken voor een verzameling gerelateerde functies. Het een sluit het ander niet uit. Dat zijn gewoon class methods, in tegenstelling tot object methods: ze doen niks met een object maar staan min of meer op zichzelf. In php of c++ of welke taal dan ook kan dat natuurlijk ook wel.
slakkiewoensdag 23 januari 2008 @ 08:15
Waarom bless je niet $args? $self is een copy ervan.

Volgende keer komt inheritance aan bod?
Farenjiwoensdag 23 januari 2008 @ 09:22
quote:
Op woensdag 23 januari 2008 08:15 schreef slakkie het volgende:
Waarom bless je niet $args? $self is een copy ervan.
Omdat ik controle wil hebben over wat ik in mijn object krijg, en je niet zomaar bagger erin kan stoppen.
Je kan ook argumenten hebben die je niet, of in een andere vorm in je object wil stoppen.
Farenjiwoensdag 23 januari 2008 @ 21:47
Weinig respons, leest iemand dit eigenlijk wel? Anyway ik ga gewoon door. :)

Vorige keer hebben we de structuur van het object DataRow gedefinieerd, met als properties de velden ID, fields en dbTable. Het is een generieke class voor een rij gegevens uit een databasetabel. Dit klinkt allemaal nogal abstract. Laten we het dus wat minder abstract maken, en een echte applicatie maken. We gaan een databasesysteem maken om een muziekverzameling in op te slaan!

Hiervoor moeten we een database met wat tabellen maken. We houden het simpel, 3 tabellen die een onderlinge relatie hebben:

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
CREATE DATABASE ooptutorial;
USE ooptutorial;

CREATE TABLE `albums` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `image_url` varchar(255) default NULL,
  `release_date` date NOT NULL,
  PRIMARY KEY  (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE `artists` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY  (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


CREATE TABLE `songs` (
  `ID` int(10) unsigned NOT NULL auto_increment,
  `title` varchar(255) default NULL,
  `albumID` int(10) unsigned default NULL,
  `artistID` int(10) unsigned default NULL,
  PRIMARY KEY  (`ID`),
  KEY `fk_artistID` (`artistID`),
  KEY `fk_albumID` (`albumID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


En nu we tabellen hebben, moeten we een verbinding maken tussen ons DataRow object en de database. We maken dus de objectmethods "load" en "save" aan.

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
sub load {
   my $self = shift;
   
   $args = {
      reload   => 0,
      @_,
   };
   
   return unless ($self->{ID} && $self->{dbTable});   # zonder ID of dbTable kunnen we niet laden! 

   if (!$self->{isLoaded} || $args->{reload}) {   # alleen laden als ie nog niet is geladen, of als reload=1
      
      # construeer de sql
      # "join" werkt net als implode in php, hier wordt automatisch de juiste sql query mee gemaakt
      my $sql = "select " . join (",", keys %{$self->{fields}}) . " from " . $self->{dbTable} . " where ID = ?;";
      
      # query voorbereiden en uitvoeren met $self->{ID} als argument
      my $sth = $dbh->prepare($sql);
      $sth->execute($self->{ID}) or die $sth->errstr;
      
      # als er een resultaat is, vul dan het object met data uit de DB
      if (my $ref = $sth->fetchrow_hashref()) {
         $self->setField( $ref );
         $self->{isLoaded} = 1;
         return $self;   # als returnvalue het object zelf
      }
   }
}


sub save {
   my $self = shift;
   
   return unless ($self->{dbTable});   # zonder tabel kunnen we niet opslaan!

   if ($self->{ID}) {   # het object bestaat al in de database, doe dus een replace incl ID
      my $sql = "
         replace into " . $self->{dbTable} . " (". join(",", sort keys %{$self->{fields}}) . ") 
         values (?, ";   # placeholder voor ID
      $sql .=  ' ?,' x scalar keys %{$self->{fields}};   # maak een placeholder voor elk field in $self->{fields}
      chop $sql;   # de laatste komma willen we niet, dus erafchoppen
      $sql .= ');';
      my $sth = $dbh->prepare($sql);
      $sth->execute($self->{ID}, map { $self->getField( field => $_ ) } sort keys %{$self->{fields}} )
         or die $sth->errstr;
      
   } else {   # nog geen ID bekend, het object bestaat dus nog niet, doe een insert
      my $sql = "
         insert into " . $self->{dbTable} . " (" .join(",", sort keys %{$self->{fields}}) . ") 
         values (";
      $sql .= '?,' x scalar keys %{$self->{fields}};
      chop $sql;
      $sql .= ');';
      my $sth = $dbh->prepare($sql);
      $sth->execute( map { $self->getField( field => $_ ) } sort keys %{$self->{fields}} )
         or die $sth->errstr;
      $self->{ID} = $sth->{mysql_insertid};
   }

   return $self->{ID};   
   
}


We missen nog wat functies die hierboven gebruikt worden, om de velden en hun inhoud uit te lezen en te wijzigen:

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
sub getField {
   my $self = shift;
   my $args = {
      field   => undef,
      @_,
   };
   $self->load;   # als het object nog niet geladen is dan doen we dat hier alsnog
   return $self->{fields}->{$args->{field}};
}

sub getFields {   # geeft de hele fields hashref terug
   my $self = shift;
   $self->load;   
   return $self->{fields};
}


sub setField {   # wijzig een of meerdere velden in het object
   my $self = shift;
   my $fields = {};
   
   if (scalar @_ > 1) {   # meer dan 1 element: list met name => value paren
      $fields = {@_};
   } elsif (scalar @_ == 1) {   # 1 element: een hashref met name => value paren
      ($fields) = @_;
   }   
   
   for my $key (keys %{$fields}) {
      if (exists $self->{fields}->{$key}) {   # zet het veld alleen als het gedefinieerd is
         $self->{fields}->{$key} = $fields->{$key};
         $self->{lastModified} = time;
      }
   }   
}



Zo! Nu hebben we een echt functioneel object, waar we al best wat mee kunnen. En dat object schreeuwt erom om ge'subclass'd te worden.

Subclassen betekent: maak een nieuwe class die alle eigenschappen van de parentclass "overerft". Het leuke aan subclasses is dat je alles kan herdefinieren. Zo begin je dus al met een basis, waarvanuit je verder kan bouwen, het principe van building blocks. Je wil namelijk geen dubbel werk doen!

We maken een nieuwe class aan in het bestand Artist.pm. Hierin zetten we:

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
package Artist;

use vars  qw(@ISA);

use strict;

@ISA = qw(DataRow);   # importeer alle functies van de package DataRow

use DataRow;

sub new {
   # we herdefinieren de constructor functie
   my $classname = shift   # deze is hier dus "Artist"!
   
   my $args = {
      name   => 'unnnamed artist',
      @_,
   };
   
   # de basis van een Artist object is een DataRow object
   my $self = DataRow->new(
      fields   => {
         name   => $args->{name},
      },
      @_,      # in @_ zitten ook alle evt overige argumenten, die gooien we gewoon allemaal mee naar DataRow->new
   );
   
   bless $self, $classname;   # maak er nu een echt Artist object van
}


1;


Zo! Dat was dat! We kunnen nu makkelijk een nieuw Artist object maken met:

1
2
3
4
5
6
7
#!/usr/bin/perl

use strict;
use Artist;

my $artist = Artist->new( name => 'Miles Davis' );
$artist->save;


De setField en save methodes heeft Artist gewoon geerfd van DataRow, die hoeven we niet te herdefinieren.

Datzelfde truukje kunnen we natuurlijk ook met Album en Song doen:

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
package Album;

use vars  qw(@ISA);

use strict;

@ISA = qw(DataRow);   # importeer alle functies van de package DataRow

use DataRow;

sub new {
   # we herdefinieren de constructor functie
   my $classname = shift   # deze is hier dus "Album"!
   
   my $args = {
      title         => 'unnnamed album',
      image_url      => '/albums/default.jpg',
      release_date   => undef,
      @_,
   };
   
   my $self = DataRow->new(
      fields   => {
         title         => $args->{title},
         image_url      => $args->{image_url},
         release_date   => $args->{release_date},
      },
      @_,      # in @_ zitten ook evt andere argumenten, die gooien we gewoon mee naar DataRow->new
   );
   
   bless $self, $classname;   # maak er nu een echt Album object van
}


1;


En hoe het nou met Song.pm moet, kun je vast wel zelf bedenken. :P

Volgende keer gaan we kijken wat de samenhang tussen deze objecten is, en hoe we ze handig met elkaar kunnen laten samenwerken! Dan begint de echte kracht pas! *O*
slakkiewoensdag 23 januari 2008 @ 22:03
Overigens, voor de mensen die geen perl snappen, het cijfer 0, de string 0 of string '' (leeg dus) gelden als undefined binnen Perl.

1
2
3
4
5
6
7
8
9
10
11
12
my @array = ( 0, "0", "", undef, 1, 2) ;
foreach my $value (@array) {
  if ($value) {
    printf "TRUE: $value \n";
  } else {
    if (defined $value) {
      print "FALSE: '$value'\n";
      next;
    }
    print "FALSE\n";
  }
}


Dit even ter verduidelijking, aangezien er wellicht wat mensen gaan zeggen, maar ID was 0 hoe kan ie dan saven?!?

[ Bericht 36% gewijzigd door slakkie op 24-01-2008 13:29:24 ]
Anthraxxdonderdag 24 januari 2008 @ 10:30
Persoonlijk vind ik het handiger om OOP uit te leggen met een taal die daar wel voor ontworpen is, en niet met een taal dat het er later bij heeft gehackt (zoals PHP en Perl).
Aibmidonderdag 24 januari 2008 @ 10:34
quote:
Op donderdag 24 januari 2008 10:30 schreef Anthraxx het volgende:
Persoonlijk vind ik het handiger om OOP uit te leggen met een taal die daar wel voor ontworpen is, en niet met een taal dat het er later bij heeft gehackt (zoals PHP en Perl).
Eens. Sowieso denk ik dat mensen die het bovenstaande een beetje kunnen lezen al OO kunnen programmeren. Als je een complete leek bent snap je er niks van, want dan snap je nog geeneens wat een functie is, wat return types zijn, wat parameters zijn, etcetera.

Misschien dat een enkele PHP-scripter dit niet snapt, maar dan vraag ik me af waarom dit voorbeeld niet in PHP is
Farenjidonderdag 24 januari 2008 @ 10:40
quote:
Op donderdag 24 januari 2008 10:34 schreef Aibmi het volgende:

[..]

Eens. Sowieso denk ik dat mensen die het bovenstaande een beetje kunnen lezen al OO kunnen programmeren. Als je een complete leek bent snap je er niks van, want dan snap je nog geeneens wat een functie is, wat return types zijn, wat parameters zijn, etcetera.
Deze tutorial is niet voor totale leken maar voor mensen die wel een beetje kunnen scripten maar het OO denken niet onder de knie krijgen.
quote:
Misschien dat een enkele PHP-scripter dit niet snapt, maar dan vraag ik me af waarom dit voorbeeld niet in PHP is
Omdat PHP een ranzige kuttaal is?
Anthraxxdonderdag 24 januari 2008 @ 10:40
Nee, Perl is lekker consistent . Dezelfde dooddoener kunnen we gebruiken voor Perl.
slacker_nldonderdag 24 januari 2008 @ 11:18
Perl is geen ranzige kuttaal! Vieze PHP'er.
Litphodonderdag 24 januari 2008 @ 11:19
Wellicht dat de PHP-discussie . ergens anders heen kan? Anders zie ik weinig hoop voor dit topic in ieder geval .
Anthraxxdonderdag 24 januari 2008 @ 11:30
quote:
Op donderdag 24 januari 2008 11:18 schreef slacker_nl het volgende:
Perl is geen ranzige kuttaal! Vieze PHP'er.
PHP'er? Ik?
Farenjidonderdag 24 januari 2008 @ 11:43
Perl is mijn lievelingstaal, niet zonder reden, maar we gaan hier niet discussieren daarover. Wat ik me afvraag: moet ik hier nog tijd in steken of heeft iedereen zoiets van: "mwoa"? Want dan houd ik mijn kwaliteitscode wel lekker voor mezelf!
Farenjidonderdag 24 januari 2008 @ 20:02
Ok, dat is dan duidelijk.
Stelletje php dummies.
Aaargh!donderdag 24 januari 2008 @ 20:19
quote:
Op donderdag 24 januari 2008 11:43 schreef Farenji het volgende:
Perl is mijn lievelingstaal, niet zonder reden, maar we gaan hier niet discussieren daarover.
Perl heeft zeker z'n toepassingen, maar om het nu een mooie taal te noemen...
quote:
Wat ik me afvraag: moet ik hier nog tijd in steken of heeft iedereen zoiets van: "mwoa"? Want dan houd ik mijn kwaliteitscode wel lekker voor mezelf!
Tja, als je echt OO wilt uitleggen dan kan je dat beter doen aan de hand van b.v. Java. Met als voordeel dat dat een taal is die echt bedoelt is om OO in te proggen en dat de syntax ook nog enigszins leesbaar is, zeker in vergelijking met die Perl meuk.
SuperRembovrijdag 25 januari 2008 @ 13:39
Als je OO wil uitleggen dan zou ik met een eenvoudiger voorbeeld beginnen. Ik ken Perl een beetje, maar hier zie ik door de (cryptische) string/hash/etc handling de OO niet meer. Al het gedoe als "my $classname = shift" in een "constuctor" en "my $self = shift" in een "method" sneeuwt daardoor onder.

Voor dat je met die lappen code komt, had je eigenlijk moeten uitleggen hoe de objectenstructuur in gedachten hebt. Een diagrammetje erbij zou verhelderend werken. Ik vraag me af of deze structuur OO-technisch gezien wel handig in elkaar steekt.
quote:
Op donderdag 24 januari 2008 20:19 schreef Aaargh! het volgende:
Tja, als je echt OO wilt uitleggen dan kan je dat beter doen aan de hand van b.v. Java. Met als voordeel dat dat een taal is die echt bedoelt is om OO in te proggen en dat de syntax ook nog enigszins leesbaar is, zeker in vergelijking met die Perl meuk.
Voordeel van een echt OO taal is dat alles OO is, dus zonder losse functies als join/chop/keys die natuurlijk methoden op een object horen te zijn.
Litphovrijdag 25 januari 2008 @ 13:43
quote:
Op vrijdag 25 januari 2008 13:39 schreef SuperRembo het volgende:
Voordeel van een echt OO taal is dat alles OO is, dus zonder losse functies als join/chop/keys die natuurlijk methoden op een object horen te zijn.
Of primitives .
Anthraxxvrijdag 25 januari 2008 @ 16:09
quote:
Op donderdag 24 januari 2008 20:02 schreef Farenji het volgende:
Ok, dat is dan duidelijk.
Stelletje php dummies.
PHP is nog triester dan Perl.
Farenjivrijdag 25 januari 2008 @ 20:21
quote:
Op vrijdag 25 januari 2008 13:39 schreef SuperRembo het volgende:
Als je OO wil uitleggen dan zou ik met een eenvoudiger voorbeeld beginnen. Ik ken Perl een beetje, maar hier zie ik door de (cryptische) string/hash/etc handling de OO niet meer. Al het gedoe als "my $classname = shift" in een "constuctor" en "my $self = shift" in een "method" sneeuwt daardoor onder.
Tja ik vrees inderdaad dat ik al te lang met perl werk - dan vergeet ik dat vreselijke geworstel dat ik in het begin had om bijv iets als references te begrijpen. Maar als ik helemaal onderaan begonnen met uitleggen was dan had ik 10 keer zoveel ruimte nodig gehad. Dan verwijs ik liever naar Leer hier Perl
quote:
Voor dat je met die lappen code komt, had je eigenlijk moeten uitleggen hoe de objectenstructuur in gedachten hebt. Een diagrammetje erbij zou verhelderend werken. Ik vraag me af of deze structuur OO-technisch gezien wel handig in elkaar steekt.
Dit is het objectmodel:



Pijlen met stippellijnen zijn dependencies, pijlen met ononderbroken lijnen is inheritance. Eigenlijk is er niet echte dependency tussen de Artist, Song en Album, want alle objecten worden in de superclass gecreeerd dmv een factory pattern.

Het datamodel was niet helemaal goed doordacht want het is een database voor verzamelalbums geworden.
De relaties tussen de verschillende objecten zijn gedefinieerd dmv generieke getChildren en getParent functies in DataRow. De functies in de subclasses zijn gewoon wrappers hieromheen, bijv in Artist.pm cq Song.pm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sub getSongs {
   my $self = shift;
   
   return $self->SUPER::getChildren( 
      key    => 'artistID',
      class    => 'Song',
   );   
   
}

sub getAlbum {
   my $self = shift;
   
   return $self->SUPER::getParent( 
      key    => 'albumID',
      class    => 'Album',
   );   
   
   
}


Of nog een mooi onleesbaar staaltje van perl magic, in Artist.pm:

1
2
3
4
5
6
7
8
sub getAlbums {
   my $self = shift;
   
   my @albums = map { $_->getAlbum } @{$self->getSongs};
   
   return \@albums;
      
}



Het resultaat van deze functies is dus een array met Song objecten, respectievelijk een Album object.
quote:
Voordeel van een echt OO taal is dat alles OO is, dus zonder losse functies als join/chop/keys die natuurlijk methoden op een object horen te zijn.
Niks mis met null termininated literals.
super-muffinvrijdag 25 januari 2008 @ 20:43
Jammer dat dit forum geen syntax highlighting heeft voor Perl. Zou het een stuk duidelijker zijn.
Aaargh!vrijdag 25 januari 2008 @ 21:22
Die meuk is gewoon onleesbaar, zeker voor iemand die niks van OO weet.

Ik bedoel, ik ben software engineer, ik droom zo ongeveer in OO, ik doe de hele dag niets anders dan proggen, 40+ uur per week, en ik moet nog ff heel goed kijken om die Perl meuk te ontcijferen. Hoe denk je dat dit op een beginner overkomt ? Die werpt een blik op die code en geeft 't onmiddelijk op. Ik denk dat de configuratie file van Sendmail nog duidelijker is dan het gemiddelde Perl script.

Dit gaat nooit duidelijk zijn voor een beginner, kies alsjeblieft een duidelijkere taal als je dit wilt uitleggen.