[oop] eigenschappen worden niet getoond/construct
Ik ben net begonnen met OOP, en ik was een klasse aan het schrijven om een soort diertjes te maken.
Dit is de klasse:
Code (php)
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
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
<?php
class animal
{
//initalisatie
private $maxvoer = 30;
private $gevoerd = 10;
private $health = 100;
private $leeftijd = 0;
private $gemaakt;
private $soort;
private $name;
//contructor
public function __construct() {}
//method: toon_eigenschappen
function toon_eigenschappen()
{
//initalisatie
$this->leeftijd = tijd_verschil(time(),(real)$this->gemaakt);
$soort = $this->soort;
$gemaakt = $this->gemaakt;
$leeftijd = $this->leeftijd;
$naam = $this->name;
$health = $this->health;
$maxvoer = $this->maxvoer;
$voer = $this->gevoerd;
$content = "
<h3>$naam</h3>
Soort: $soort.<br />
Gemaakt: $gemaakt.<br />
Leeftijd: $leeftijd.<br />
Gezondheid: $health.<br />
Maxvoer: $maxvoer.<br />
Gevoerd: $voer.<br />
Naam: $naam.
";
echo $content;
}
}
class metrix extends animal
{
public function __construct($naam,$maxeten=30)
{
$this->maxvoer = $maxeten;
$this->gemaakt = 1000000;
$this->soort = 'metrix';
$this->name = $naam;
}
}
function tijd_verschil($nu,$toen)
{
if ( (!is_numeric($nu) ) || (!is_numeric($toen) ) )
{
trigger_error('Error in tijd_verschil: een van de tijden is geen UNIX tijd, $toen = '.$toen,E_USER_NOTICE);
return;
}
$resultaat = $nu - $toen;
$resultaat = date('G \u\r\e\n i \m\i\n\u\t\e\n \e\n s \s\e\c\o\n\d\e\n',$resultaat);
return $resultaat;
}
?>
class animal
{
//initalisatie
private $maxvoer = 30;
private $gevoerd = 10;
private $health = 100;
private $leeftijd = 0;
private $gemaakt;
private $soort;
private $name;
//contructor
public function __construct() {}
//method: toon_eigenschappen
function toon_eigenschappen()
{
//initalisatie
$this->leeftijd = tijd_verschil(time(),(real)$this->gemaakt);
$soort = $this->soort;
$gemaakt = $this->gemaakt;
$leeftijd = $this->leeftijd;
$naam = $this->name;
$health = $this->health;
$maxvoer = $this->maxvoer;
$voer = $this->gevoerd;
$content = "
<h3>$naam</h3>
Soort: $soort.<br />
Gemaakt: $gemaakt.<br />
Leeftijd: $leeftijd.<br />
Gezondheid: $health.<br />
Maxvoer: $maxvoer.<br />
Gevoerd: $voer.<br />
Naam: $naam.
";
echo $content;
}
}
class metrix extends animal
{
public function __construct($naam,$maxeten=30)
{
$this->maxvoer = $maxeten;
$this->gemaakt = 1000000;
$this->soort = 'metrix';
$this->name = $naam;
}
}
function tijd_verschil($nu,$toen)
{
if ( (!is_numeric($nu) ) || (!is_numeric($toen) ) )
{
trigger_error('Error in tijd_verschil: een van de tijden is geen UNIX tijd, $toen = '.$toen,E_USER_NOTICE);
return;
}
$resultaat = $nu - $toen;
$resultaat = date('G \u\r\e\n i \m\i\n\u\t\e\n \e\n s \s\e\c\o\n\d\e\n',$resultaat);
return $resultaat;
}
?>
Dit is de aanroep:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="HTML-Kit Tools" />
</head>
<body>
<?php
require_once 'animals.class.php';
$nu = new metrix('Hallo');
$nu->toon_eigenschappen();
?>
</body>
</html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="HTML-Kit Tools" />
</head>
<body>
<?php
require_once 'animals.class.php';
$nu = new metrix('Hallo');
$nu->toon_eigenschappen();
?>
</body>
</html>
Nu krijg ik dit als resultaat:
Quote:
Soort: .
Gemaakt: .
Leeftijd: 15 uren 48 minuten en 49 seconden.
Gezondheid: 100.
Maxvoer: 30.
Gevoerd: 10.
Naam: .
Gemaakt: .
Leeftijd: 15 uren 48 minuten en 49 seconden.
Gezondheid: 100.
Maxvoer: 30.
Gevoerd: 10.
Naam: .
De tijd klopt dus niet en soort, gemaakt en naam worden niet getoond.
Ik kan de fout niet kunnen vinden (als beginner), dus kunnen jullie het vinden?
Waar het fout gaat is dat je in de class animal een aantal waardes definieert als private. Deze waardes zijn dus alleen toegankelijk door de class animal en niet door classes die daarvan afstammen. De functie toon_eigenschappen() haalt $this->name op, maar in de ge-extende class metrix bestaat dat attribuut niet, omdat private attributen niet worden meegenomen bij het extenden. Als je in de animal class het woordje private voor je attribuutnamen verandert in protected (mag worden gebruikt door huidige class en alle afstammelingen, maar niet daarbuiten of public (mag altijd door alles worden gebruikt), zou het wel moeten werken.
Bedankt, wat ben ik toch stom.
Ach, beginnersfout.
Behalve dan dat je properties nooit public moet maken (en wat mij betreft ook nooit protected). Het ingeven en uitlezen van data doe je via setters en getters. Die methodes maak je vervolgens protected of public en op die manier kan je de waarde van een property veranderen. Hiermee voorkom je dat je later in de problemen komt als je de structuur van je class wilt aanpassen. Als er namelijk een andere class direct in je properties kan schrijven dan kan je nagenoeg nooit meer testen of je veranderingen wel werken.
Erwin H op 24/05/2013 15:19:08:
Behalve dan dat je properties nooit public moet maken (en wat mij betreft ook nooit protected). Het ingeven en uitlezen van data doe je via setters en getters. Die methodes maak je vervolgens protected of public en op die manier kan je de waarde van een property veranderen. Hiermee voorkom je dat je later in de problemen komt als je de structuur van je class wilt aanpassen. Als er namelijk een andere class direct in je properties kan schrijven dan kan je nagenoeg nooit meer testen of je veranderingen wel werken.
Er zijn bijna geen objecten uit de klasse animal, dat is dus niet van belang.
Maar goed, je komt er vanzelf achter als je wat langer bezig bent. Jammer dan wel dat je dan veel meer werk hebt aan het corrigeren van dit soort beginnersfouten dan dat je er nu aan zou hebben.
Toevoeging op 24/05/2013 15:30:06:
Voorbeeld: ik gok dat je over niet al te lange tijd erachter komt dat het property 'leeftijd' niet zo slim is. Dat property zal je namelijk elk jaar moeten aanpassen.... Als je nu geboortedatum (of jaar) opslaat hoeft dat. Dan kan je leeftijd gewoon uitrekenen.
Heb je nu een class die direct dat property uitleest dan heb je een probleem. Dan moet je ook die class aanpassen. Maar, als je het via een getter laat lopen dan hoef je alleen de getter aan te passen (die nu de leeftijd zal uitrekenen) en alle andere classes zullen er niets van merken. Scheelt je een hoop tijd en ellende kan ik je verzekeren.
Zo vind ik het best acceptabel dat je het object "Person" een public property "name" geeft, die je dan niet hoeft op te vragen d.m.v. $Person->getName(), maar d.m.v. $Person->name;. Duidelijk genoeg naar mijn smaak :). Maar ieder z'n voorkeur. Op den duur kom je er vanzelf achter wat wel en niet werkt. Als je je class gaat extenden kun je er altijd op dat moment voor kiezen om nog een getter functie te schrijven als je die nodig blijkt te gaan hebben.
Leeftijd wordt door functie tijd_verschil() uitgerekend, op bassis van de geboortedatum (later uit database) en time(), daar hoef ik dus niks aan aan te passen.
Stephan G op 24/05/2013 15:54:12:
Hmmm ja zo hier en daar kan dat handig zijn, maar dat vind ik zeker niet altijd nodig. Voor sommige properties is het om het even of je nu direct een getter/setter schrijft, of dat je het later doet. Of je die tijd er nou nu insteekt, of later: het blijft evenveel tijd, maar wie weet levert het je op de korte termijn tijdswinst op als je het niet direct doet.
Zo vind ik het best acceptabel dat je het object "Person" een public property "name" geeft, die je dan niet hoeft op te vragen d.m.v. $Person->getName(), maar d.m.v. $Person->name;. Duidelijk genoeg naar mijn smaak :). Maar ieder z'n voorkeur. Op den duur kom je er vanzelf achter wat wel en niet werkt. Als je je class gaat extenden kun je er altijd op dat moment voor kiezen om nog een getter functie te schrijven als je die nodig blijkt te gaan hebben.
Zo vind ik het best acceptabel dat je het object "Person" een public property "name" geeft, die je dan niet hoeft op te vragen d.m.v. $Person->getName(), maar d.m.v. $Person->name;. Duidelijk genoeg naar mijn smaak :). Maar ieder z'n voorkeur. Op den duur kom je er vanzelf achter wat wel en niet werkt. Als je je class gaat extenden kun je er altijd op dat moment voor kiezen om nog een getter functie te schrijven als je die nodig blijkt te gaan hebben.
Je begrijpt volgens mij de insteek niet. Als je een class maakt met public properties dan kan je vast 'later' nog wel een setter schrijven. Maar ga je dan ook al je andere code langs om even te kijken waar precies dat property direct wordt aangesproken? Heb je enig idee hoeveel code je dan langs moet gaan? Als je het meteen goed doet heb je nooit(!) last van.
Maar goed, ik heb deze discussie inmiddels meerdere keren gevoerd op dit forum en hoewel ik begrijp dat meningen verschillen doet het me altijd deugd om mensen om te zien gaan naar private properties omdat ze er later toch achter komen dat dat toch echt slimmer is. Andersom ben ik het nog nooit tegen gekomen. Dus: doe vooral wat je wilt, maar denk hier nog eens aan terug als je de volgende keer weer al je code door moet spitten opzoek naar die ene plek waar dat property werd aangesproken dat je nu weg wilt halen....
Als PHP een foutmelding ziet zie je toch gelijk het bestand en regelnummer?
Ik heb meerdere websites draaien gebouwd op hetzelfde framework. Als ik iets in mijn framework verander en dat uitrol dan wil ik niet alle websites en alle pagina's en alle test cases langs moeten gaan om te zien of ik ergens misschien een foutmelding krijg. Los van het feit dat je nog steeds al die foutmeldingen moet oplossen. Als je het gewoon direct goed doet hoef je NIETS op te lossen.
En ik heb wel wat ervaring (vooral met databases en mysql), alleen niet op het gebied van OOP.
Zoals ik zei, doe vooral wat je wilt. Ik geef een tip waarvan ik weet dat veel mensen die in eerste instantie niet willen aannemen, maar er vaak later toch op terugkomen. Aan jou de keuze wat je ermee doet...
Gewijzigd op 24/05/2013 16:40:53 door Stephan G
Erwin geeft hier zeer nuttige tips. Wat je ermee doet moet je zelf weten, maar ik zou er gebruik van maken.
En van schade en schande wordt men wijs.
Initialiseren? Ik las laatst in een [OOP]-boek dat initialiseren zo "not done" is dat de auteur niet eens wilde uitleggen waarom... Iemand die daarover iets meer kan zeggen?
Ik kan me verschillende situaties voorstellen waarbij een init() methode nuttig kan zijn.
Maar meer nog; zo kan ik ook boeken schrijven.
Doe gewoon wat ik zeg. Waarom? Daarom!
George Schlossnagle - Advanced PHP Programming:
Handling Constructor Failure
Handling constructor failure in an object is a difficult business. A class constructor in PHP must return an instance of that class, so the options are limited:
• You can use an intialized attribute in the object to mark it as correctly initialized.
• You can perform no initialization in the constructor.
• You can throw an exception in the constructor.
The first option is very inelegant, and we won't even consider it seriously. The second option is a pretty common way of handling constructors that might fail. […]
Handling constructor failure in an object is a difficult business. A class constructor in PHP must return an instance of that class, so the options are limited:
• You can use an intialized attribute in the object to mark it as correctly initialized.
• You can perform no initialization in the constructor.
• You can throw an exception in the constructor.
The first option is very inelegant, and we won't even consider it seriously. The second option is a pretty common way of handling constructors that might fail. […]
Gewijzigd op 23/07/2013 15:15:29 door Ward van der Put
Probeert hij hiermee niet te zeggen dat je een attribuut zoals $initialized aanmaakt en daarin true of false zet, en deze dan uitlezen waar je hem zou gebruiken?
Koen Vlaswinkel op 23/07/2013 15:23:12:
Probeert hij hiermee niet te zeggen dat je een attribuut zoals $initialized aanmaakt en daarin true of false zet, en deze dan uitlezen waar je hem zou gebruiken?
Zoiets zal het inderdaad zijn, alleen vraag ik me af waarom je dat niet zou doen in bijvoorbeeld de constructor of een aparte init(). Als ik mijn auto initialiseer, gaan er ook na verschillende controles lampjes branden. En een geel lampje of, nog erger, rood lampje is niet okay, maar dat betekent nog niet dat de bijbehorende eigenschappen of het gehele object volledig onbruikbaar zijn.