[oop] abstract vraagje
Met het strategy pattern heb je 1 klasse: Vehicle (voertuig, hoe je het beestje noemen wil) Dan hebben we vele andere strategy klassen die het gedrag van dit voortuig bepalen. Bijv:
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
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
<?php
interface VehicleHorn
{
public function alert();
public function normal();
}
class Toot implements VehicleHorn
{
public function alert() { return 'TOOOEET!!'; }
public function normal() { return 'toettoet'; }
}
class Bell implements VehicleHorn
{
public function alert() { return 'TRIIING!'; }
public function normal() { return 'tringtring'; }
}
class NoHorn implements VehicleHorn
{
public function alert() { return '*silence*'; }
public function normal() { return '*even more silence*'; }
}
class Vehicle
{
protected $horn;
public function __construct(VehicleHorn $horn)
{
$this->horn = $horn;
}
public function useHorn($alert = false)
{
return $alert ? $this->horn->alert() : $this->horn->normal();
}
}
$car = new Vehicle(new Toot);
echo $car->useHorn(); //> 'toettoet'
$bicycle = new Vehicle(new Bell);
echo $car->useHorn(true); //> 'TRIIING!'
$oldBicycle = new Vehicle(new NoHorn);
echo $car->useHorn(); //> '*silence*'
?>
interface VehicleHorn
{
public function alert();
public function normal();
}
class Toot implements VehicleHorn
{
public function alert() { return 'TOOOEET!!'; }
public function normal() { return 'toettoet'; }
}
class Bell implements VehicleHorn
{
public function alert() { return 'TRIIING!'; }
public function normal() { return 'tringtring'; }
}
class NoHorn implements VehicleHorn
{
public function alert() { return '*silence*'; }
public function normal() { return '*even more silence*'; }
}
class Vehicle
{
protected $horn;
public function __construct(VehicleHorn $horn)
{
$this->horn = $horn;
}
public function useHorn($alert = false)
{
return $alert ? $this->horn->alert() : $this->horn->normal();
}
}
$car = new Vehicle(new Toot);
echo $car->useHorn(); //> 'toettoet'
$bicycle = new Vehicle(new Bell);
echo $car->useHorn(true); //> 'TRIIING!'
$oldBicycle = new Vehicle(new NoHorn);
echo $car->useHorn(); //> '*silence*'
?>
Ozzie PHP op 23/02/2014 19:57:01:
Jongens, de discussie dwaalt een beetje af. Ik ga mijn vraag nog een keer stellen en ben dan benieuwd naar jullie antwoord. We vergeten even het hele database gebeuren.
Stel we hebben een auto en een fiets. Beiden zijn voertuigen, dus het lijkt me dan zinvol om een abstracte class Voertuig te maken. Mee eens?
Nu is mijn vraag wat er in die abstracte class thuis hoort.
We kunnen stellen dat ieder voertuig kan sturen, remmen en gasgeven. Dus in de voertuig class kunnen we de methods stuurLinks, stuurRechts, rem en gas zetten. Maar... hoort daar bijv. ook een method getNummerbord in? Veel voertuigen hebben een nummerbord, maar een fiets bijvoorbeeld niet. Plaatsen we de getNummerbord method in de voertuig class en accepteren we dat het mogelijk is dat iemand van een fietsobject het nummerbord opvraagt? Of plaatsen we getNummerbord alleen in de auto class?
Stel we hebben een auto en een fiets. Beiden zijn voertuigen, dus het lijkt me dan zinvol om een abstracte class Voertuig te maken. Mee eens?
Nu is mijn vraag wat er in die abstracte class thuis hoort.
We kunnen stellen dat ieder voertuig kan sturen, remmen en gasgeven. Dus in de voertuig class kunnen we de methods stuurLinks, stuurRechts, rem en gas zetten. Maar... hoort daar bijv. ook een method getNummerbord in? Veel voertuigen hebben een nummerbord, maar een fiets bijvoorbeeld niet. Plaatsen we de getNummerbord method in de voertuig class en accepteren we dat het mogelijk is dat iemand van een fietsobject het nummerbord opvraagt? Of plaatsen we getNummerbord alleen in de auto class?
Dit is een hele goeie vraag, zoals jij een paar berichten terug al zei: zijn er twee mogelijke manier van handelen. Welke manier van handelen het wij aanraden ligt naar mijn mening aan het aantal methoden/variabelen die fiets en auto van elkaar verschillen. Wanneer je er dus veel verschil is tussen auto en fiets zou ik 2 aparte klassen maken die ook mappen naar een Fiets/Auto tabel waarna die verwijzen naar een Voertuig tabel.
Wanneer de verschillen tussen een auto en fiets klein zijn dan zal ik een klasse en een tabel Voertuig ook accepteren.
@Wouter: thanks voor je voorbeeld. Ik snap ook wel wat je bedoelt. Maar wat doe je dan met een voertuig wat wel of niet een nummberbord heeft? En ieder voertuig moet sturen, remmen en gasgeven. Dan gebruik je daar toch wel een abstracte class voor die je extend?
Je extend niet, je hebt maar 1 klasse: Vehicle.
En om maar in het voorbeeld te blijven, ieder voertuig moet sturen, remmen en gasgeven. Maar als ik rem op een fiets doe ik dat met mijn handen en in een auto met mijn voet. Gasgeven doe ik op de fiets door als een gek rondjes te maken met mijn voet en in een auto door een pedaal door de vloer te drukken. Als ik stuur in een auto doe ik dat door het stuur te draaien, zit ik in een vliegtuig dan doe ik dat weer met 2 pedaaltjes en zit ik in een helikopter dan doe ik dat door een knuppel alle hoeken van de cabine te laten zien.
Zit ik op een segway dan doe ik dit allemaal door mijn lichaam te bewegen.
Dat alles iets moet kunnen hoeft niks te zeggen dat de manier waarop overal hetzelfde is. En als je ergens niet zeker van kan zijn dan mag je in de OO wereld jezelf er zeker niet op limiteren.
Gelukkig zijn er nog wel dingen die zeker zijn. Een User heeft bijv. altijd een naam en geboortedag. Of die een adres heeft weet je niet. En dan komen we weer bij een ander belangrijk OO principe: Iets wat veranderd moet los worden gekoppeld van hetgeen dat constant is.
Oké... maar hoe doe je dat dan met een nummerbord?
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
$car = new Vehicle;
$car->getLicensePlate();
$bike = new Vehicle;
$bike ->getLicensePlate(); // dit klopt niet, want een fiets heeft geen nummerbord
?>
$car = new Vehicle;
$car->getLicensePlate();
$bike = new Vehicle;
$bike ->getLicensePlate(); // dit klopt niet, want een fiets heeft geen nummerbord
?>
>> En dan komen we weer bij een ander belangrijk OO principe: Iets wat veranderd moet los worden gekoppeld van hetgeen dat constant is.
Oké... je weet niet of ie een adres heeft zeg jij. Bedoel je dan dus, dat het adres NIET tot de default user class behoort?
Een fiets heeft ook een registratie nummer, die staat welliswaar niet op een nummerbord, maar hij heeft er wel 1 (waarschijnlijk ergens in het frame gegraveerd). En een marineschip heeft ook een nummerbord, maar dan niet zo'n geel ding, maar een grote naam op de zijkant van het schip. Een nummerbord in nederland is ook weer anders dan een nummerbord in frankrijk (kleur enzo...). Dus ook dit wordt doormiddel van het Strategy pattern opgelost.
>> Bedoel je dan dus, dat het adres NIET tot de default user class behoort?
Er is geen default user class, er is maar 1 user class. En inderdaad, het adres hoort daar naar mijn mening van losgekoppeld te worden.
Edit:
Even mijn laatste statement uitleggen. Ik zou een Address class linken aan een User object ipv een User object aan een Address klasse. Dus:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// jij
$person = Person::born('Jaap', new \DateTime());
$person->livesIn(Address::parse('Janpietstraat 12', Postcode::parse('1643 KS')));
$post->sendCartTo($person);
// ik
$address = Address::parse('Janpietstraat 12', Postcode::parse('1643 KS'));
$address->assignTo(Person::born('Jaap', new \DateTime()));
$post->sendCarTo($address);
?>
// jij
$person = Person::born('Jaap', new \DateTime());
$person->livesIn(Address::parse('Janpietstraat 12', Postcode::parse('1643 KS')));
$post->sendCartTo($person);
// ik
$address = Address::parse('Janpietstraat 12', Postcode::parse('1643 KS'));
$address->assignTo(Person::born('Jaap', new \DateTime()));
$post->sendCarTo($address);
?>
Gewijzigd op 23/02/2014 23:42:24 door Wouter J
Ik bedoel eigenlijk een abstract user class, bijv.
Admin extends User
Is dat niet goed?
>> Een nummerbord in nederland is ook weer anders dan een nummerbord in frankrijk (kleur enzo...). Dus ook dit wordt doormiddel van het Strategy pattern opgelost.
Ah oké :)
Nee, dat is 1 van de gevallen waarin inheritance overgebruikt wordt. Ik was vroeger een normaal lid van dit forum en nu ben ik een moderator. Ben ik nu ineens een andere persoon? Nee, ik ben nog steeds Wouter en noch mijn lichaam noch mijn ziel zijn veranderd (voor zover ik kan zien...). Waarom zou ik dan wel ineens een andere klasse zijn?
Om weer terug te komen op het principe van toen net: Het veranderlijke is hier wat de user kan, de rest blijft het zelfde. Dit is dus niet op te lossen door het veranderlijke gewoon nog een klasse op te schuiven, het zit dan nog steeds bij elkaar, alleen zie je dat in je code niet meer. Wat we echt moeten doen is wat een User mag loskoppelen van een User. Dit kun je bijv. doen door een Role object erbij te maken:
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
$ozzie = User::register('Ozzie');
$ozzie->setRoles(Role::create('READ'), Role::create('WRITE'), Role::create('EDIT_OWN'));
$wouter = User::register('Wouter');
$wouter->setRoles(Role::create('READ'), Role::create('WRITE'), Role::create('DELETE'), Role::create('EDIT'));
$guest = User::register('guest1235');
$guest->setRoles(Role::create('READ'));
?>
$ozzie = User::register('Ozzie');
$ozzie->setRoles(Role::create('READ'), Role::create('WRITE'), Role::create('EDIT_OWN'));
$wouter = User::register('Wouter');
$wouter->setRoles(Role::create('READ'), Role::create('WRITE'), Role::create('DELETE'), Role::create('EDIT'));
$guest = User::register('guest1235');
$guest->setRoles(Role::create('READ'));
?>
Bekijk trouwens ook even mijn edit, mocht je die gemist hebben.
Gewijzigd op 23/02/2014 23:49:57 door Wouter J
Maar zo zie ik het toch echt heel vaak hoor. En nee, je bent niet een ander persoon, maar je hebt bijv. andere rechten. In plaats van dat je zegt $wouter = new User(), zeg je dan $wouter = new Admin(), waarbij de Admin klasse dan meer rechten heeft dan de "default" user.
Begrijp me overigens niet verkeerd. Ik begrijp jouw insteek ook. Ik vraag me dus af, is dit een kwestie van persoonlijke voorkeur, of is het een daadwerkelijk beter dan het ander?
>> Bekijk trouwens ook even mijn edit, mocht je die gemist hebben.
Thanks, had 'm inderdaad gemist. Ik snap wat jij doet en ik snap oop de gedachtengang. Wat ik alleen niet begrijp... als je iemand een kaart wil sturen, dan ga je uit van die iemand, en niet van die iemand z'n adres. In je code wil je toch zoiets kunnen doen:
De een (1 klasse) volgt de OO basisprincipes, de ander niet.
>> als je iemand een kaart wil sturen, dan ga je uit van die iemand, en niet van die iemand z'n adres
Mijn brief gaat uit van de persoon. De enveloppe gaat uit van het adres, die je ergens hebt verkregen.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$person = ...;
$letter = Letter::write(<<<EOT
Hallo {$person->getName()},
...
EOT
);
// YellowPages#getAddressForPerson is de factory van Address object (DataMapper als het ware)
$address = YellowPages::getAddressForPerson($person);
$post->sendCardTo($address);
?>
$person = ...;
$letter = Letter::write(<<<EOT
Hallo {$person->getName()},
...
EOT
);
// YellowPages#getAddressForPerson is de factory van Address object (DataMapper als het ware)
$address = YellowPages::getAddressForPerson($person);
$post->sendCardTo($address);
?>
Merk ook op hoe veel dichter deze code bij de waarheid staat dan jouw code. Ik heb geen persoon waarvan ik het adres krijg, dat adres haal ik uit het telefoonboek of een adresboekje.
Gewijzigd op 24/02/2014 00:28:02 door Wouter J
Hehe, ja, ik snap wel wat je bedoelt. Maar ik vind het wel lastig om dat zelf zo te bedenken. En hoe zit dat dan bijv. met een e-mailadres? Koppel je die wel aan de persoon zelf?