Multiple Inheritance
Nu heb ik een classe Amfibievoertuig (waterauto) die gebaseerd moet zijn op beide bovenstaande classes, zoiets als dit:
Echter lees ik dat php geen multiple inheritance ondersteund.
Weet iemand een workaround?
Dit wordt ook wel het strategy design pattern genoemd: http://en.wikipedia.org/wiki/Strategy_pattern
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
interface VoertuigInterface;
interface DrijfbaarVoertuigInterface extends VoertuigInterface;
interface RijdbaarVoertuigInterface extends VoertuigInterface;
abstract class StuurbaarVoertuig implements VoertuigInterface;
class AutoVoertuig extends StuurbaarVoertuig implemets RijdbaarVoertuigInterface;
class WaterVoertuig extends StuurbaarVoertuig implements DrijfbaarVoertuigInterface;
class AmfibieVoertuig extends StuurbaarVoertuig implements RijdbaarVoertuigInterface, DrijfbaarVoertuigInterface;
interface DrijfbaarVoertuigInterface extends VoertuigInterface;
interface RijdbaarVoertuigInterface extends VoertuigInterface;
abstract class StuurbaarVoertuig implements VoertuigInterface;
class AutoVoertuig extends StuurbaarVoertuig implemets RijdbaarVoertuigInterface;
class WaterVoertuig extends StuurbaarVoertuig implements DrijfbaarVoertuigInterface;
class AmfibieVoertuig extends StuurbaarVoertuig implements RijdbaarVoertuigInterface, DrijfbaarVoertuigInterface;
Het strategy pattern had ik nog niet aan gedacht, is ook een mooie oplossing:
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
interface WaterGedrag;
interface VasteGrondGedrag;
interface VoertuigInterface;
class DrijfGedrag implements WaterGedrag;
class ZinkGedrag implements WaterGedrag;
...
class RijGedrag implements VasteGrondGedrag;
class SchuifGedrag implements VasteGrondGedrag;
...
class Voertuig implements VoertuigInterface;
interface VasteGrondGedrag;
interface VoertuigInterface;
class DrijfGedrag implements WaterGedrag;
class ZinkGedrag implements WaterGedrag;
...
class RijGedrag implements VasteGrondGedrag;
class SchuifGedrag implements VasteGrondGedrag;
...
class Voertuig implements VoertuigInterface;
En dan in gebruik:
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
$auto = new Voertuig();
$auto->setVasteGrondGedrag(new RijGedrag());
$auto->setWaterGedrag(new ZinkGedrag());
$boot = new Voertuig();
$boot->setVasteGrondGedrag(new SchuifGedrag());
$boot->setWaterGedrag(new DrijfGedrag());
$amfibie = new Voertuig();
$amfibie->setVasteGrondGedrag(new RijGedrag());
$amfibie->setWaterGedrag(new DrijfGedrag());
?>
$auto = new Voertuig();
$auto->setVasteGrondGedrag(new RijGedrag());
$auto->setWaterGedrag(new ZinkGedrag());
$boot = new Voertuig();
$boot->setVasteGrondGedrag(new SchuifGedrag());
$boot->setWaterGedrag(new DrijfGedrag());
$amfibie = new Voertuig();
$amfibie->setVasteGrondGedrag(new RijGedrag());
$amfibie->setWaterGedrag(new DrijfGedrag());
?>
Gewijzigd op 11/06/2013 15:01:08 door Wouter J
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
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
class Autovoertuig
{
function GaVooruit()
{
}
}
class Watervoertuig
{
function GaVooruit()
{
}
}
class Amfibievoertuig extends Autovoertuig, Watervoertuig
{
public IsModusAutoOfBoot;
function GaVooruit()
{
if (IsModusAutoOfBoot=='auto')
{
Autovoertuig.GaVooruit;
}
elseif (IsModusAutoOfBoot=='boot')
{
Watervoertuig.GaVooruit;
}
}
}
{
function GaVooruit()
{
}
}
class Watervoertuig
{
function GaVooruit()
{
}
}
class Amfibievoertuig extends Autovoertuig, Watervoertuig
{
public IsModusAutoOfBoot;
function GaVooruit()
{
if (IsModusAutoOfBoot=='auto')
{
Autovoertuig.GaVooruit;
}
elseif (IsModusAutoOfBoot=='boot')
{
Watervoertuig.GaVooruit;
}
}
}
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
trait Rijden
{
// ...
}
trait Varen
{
// ...
}
class Amfibievoertuig
{
use Rijden, Varen;
// ...
}
?>
trait Rijden
{
// ...
}
trait Varen
{
// ...
}
class Amfibievoertuig
{
use Rijden, Varen;
// ...
}
?>
Als je echter, zoals in jouw opzet, één gedeelde methode GaVooruit() hebt die bij 'boot' anders reageert dan bij 'auto', dan heb je meer twee klassen die afstammen van één en dezelfde ouderklasse. Dan krijg je eerder dit:
Code (php)
Je kunt dus omgekeerd redeneren: een auto is een amfibievoertuig dat niet kan varen en een boot is een amfibievoertuig dat niet kan rijden.
Zo krijg je het vliegende rubber eendjes verhaal, voor degene die het boek kennen. Niet doen op die manier.
Toevoeging op 11/06/2013 17:12:15:
Hank Noseman op 11/06/2013 16:29:34:
Bedankt voor de reacties. Ik bedoel eigenlijk zoiets als onderstaande. Volgens mij is zoiets wel mogelijk in Delphi en ik zoek een workaround in PHP.
Simpel, die is er dus niet.
Het logicaprobleem bij multiple inheritance is dat je een vader en een moeder hebt die beide anders lopen: ze hebben beide een methode GaVooruit(). Dat betekent dat je een kind altijd voor een keuze stelt: loop je zoals vader, zoals moeder of zoals een mengelmoes?
Met multiple traits LopenAlsEenMan en LopenAlsEenVrouw los je dat deels op.
Het alternatief is niet elegant, dat ben ik met je eens, maar dat is inherent aan het ontbreken van multiple inheritance in PHP: je bouwt beide varianten van de methode in en laat een afgeleide klasse de methode kiezen.
Zo ongebruikelijk is dat overigens natuurlijk niet: in veel constructors kun je immers ook kiezen welke "smaak" je van een klasse wilt hebben.
En daarom is het strategy patern juist zo mooi. Daarmee bereik je precies hetzelfde, het is veel flexibeler en je voorkomt dat functionaliteit op een rare manier achter blijft daar waar je het niet wil hebben.
Erwin H op 11/06/2013 17:30:36:
Goed punt. Wat vind je dan van gedeelde functionaliteit in gemeenschappelijke traits die je expliciet moet aanroepen met use?En daarom is het strategy patern juist zo mooi. Daarmee bereik je precies hetzelfde, het is veel flexibeler en je voorkomt dat functionaliteit op een rare manier achter blijft daar waar je het niet wil hebben.
Met traits heb ik nog te weinig ervaring om daar een mening over te geven. Ik ga daar zo wel even naar kijken, maar meer uitleg van jouw kant (hoe traits hierbij een rol kunnen spelen) waardeer ik wel.
Eerlijk gezegd heb ik zo mijn bedenkingen. Traits kunnen dingen doen die ik liever niet terugzie in een grootschalig project.
Neem bijvoorbeeld deze uitbreiding op een vorig voorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
trait Rijden
{
// ...
}
trait Varen
{
// ...
}
trait Vliegen
{
// ...
}
class Amfibievoertuig
{
use Rijden, Varen, Vliegen;
// ...
}
?>
trait Rijden
{
// ...
}
trait Varen
{
// ...
}
trait Vliegen
{
// ...
}
class Amfibievoertuig
{
use Rijden, Varen, Vliegen;
// ...
}
?>
Met één simpele uitbreiding van use Rijden, Varen, Vliegen kan het amfibievoertuig plotseling ook vliegen. En die trait Vliegen geeft de klasse meteen alle methoden van de trait.
Is dat nu handig of gevaarlijk? Ik weet het niet, de waarheid zal ongetwijfeld ergens in het midden liggen.
Gewijzigd op 11/06/2013 18:13:46 door Ward van der Put
Als ik zo een beetje rondlees (zonder er dus echt diep in duiken) dan krijg ik het gevoel dat traits echt alleen interessant zijn om code op 1 plek te krijgen die je op meerdere plekken nodig hebt. Op zich zou dit dus wel een juiste omstandigheid zijn. Tegelijkertijd krijg ik alleen ook het gevoel dat traits een probleem oplossen dat je alleen krijgt door een niet optimaal design. Dubbele code kan je, denk ik, beter proberen te vermijden door je klassen structuur anders te ontwerpen. Maar inderdaad, de waarheid zal ongetwijfeld genuanceerder zijn en in een halfuurtje rondsnuffelen kom ik natuurlijk ook niet alles te weten over traits.
Erwin & Ward, ik heb jullie hele discussie niet volledig gelezen, maar naar mijn mening horen traits niet thuis in de OO klassen. Traits zijn mooi in de "niet-OO klassen", zoals een Controller of Command class.