[PHP] Meerdere klassen extenden

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Mac_Cain13
  • Registratie: Juni 2003
  • Laatst online: 20-05 01:14
Goed, ik ben druk aan het PHPen en loop even op een probleempje waarvan ik de oplossing niet zie. Volgens mij is het wat vaker voorkomt bij OO programmeren, maargoed.

Het zit alsvolgt; Ik heb twee abstracte klassen:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
abstract class Element
{
    function foo()
    {
        echo "FooElement!";
    }
}

abstract class Container
{
    function bar()
    {
        echo "BarContainer!";
    }
}


Nu heb ik andere klassen die deze abstracte klassen uitbreiden:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Page extends Container
{
    function pag()
    {
        echo "PageExtendsContainer!";
    }
}

class Image extends Element
{
    function img()
    {
        echo "ImageExtendsElement!";
    }
}

class Paragraph extends Container, Element
{
    function par()
    {
        echo "Oh no, ERROR!";
    }
}

Nu is het probleem dat het laatste in PHP (net als in bijv. Java) niet mag. Het is onmogelijk meer dan 1 klasse te extenden.

In eerste instantie had ik Container extends Element. Alleen geeft dit problemen omdat niet alle Containers Elementen zijn en visa versa.
Toen kwam een extra klasse in mij op, maar ExtraKlasse extends Container, Element mag natuurlijk net zo goed niet. Dus hier kom ik ook geen steek mee verder.

Zoals ik in het begin al zei lijkt het mij dat OO programmeurs die Java, PHP of een andere taal gebruiken waarin je maar 1 klasse mag extenden gebruiken dit probleem vaker tegenkomen. Dus er zal ook vast een slimme oplossing voorbestaan! Dus als iemand de oplossing weet of slimme ideeen heeft zou ik ze graag horen! ;)

Acties:
  • 0 Henk 'm!

  • NetForce1
  • Registratie: November 2001
  • Laatst online: 07:49

NetForce1

(inspiratie == 0) -> true

Hier zijn interfaces voor uitgevonden, een klasse mag wel meerdere interfaces implementeren (NB: dit is geen vervanging voor multiple inheritance). Voor meer info over interfaces in php zie: http://www.php.net/manual/en/language.oop5.interfaces.php. Van deze interfaces kun je dan evt abstracte basis-implementaties maken.
Voor jouw Paragraph klasse zou je bijv ook een abstracte ContainerElement kunnen maken die de interfaces iContainer en iElement implementeerd. Het probleem is waarschijnlijk dat je in Page en Paragraph dezelfde logica wilt gebruiken. Misschien kun je deze logica in een aparte klasse kwijt, zodat je niet hoeft te copy-pasten.

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


  • Mac_Cain13
  • Registratie: Juni 2003
  • Laatst online: 20-05 01:14
Hmm ok, kortom is met dat er is besloten geen multiple inheritance te implementeren ook besloten dit soort dingen onmogelijk te maken. :|

Het gebruik van interfaces is mij duidelijk en daarmee kan ik inderdaad in ieder geval afdwingen dat de nodige methodes in de klassen zitten. Het nare is dat ik dan inderdaad dezelfde logica meerdere keren aan het uitschrijven ben.

Ik zat te denken hoe ik het dan nu waarschijnlijk het best kan oplossen en heb snel even wat in elkaar ge-uml-t. (Het is laat dus ws heb ik alle pijltjes de verkeerde kant op, maar het is een impressie. :P)

Afbeeldingslocatie: http://mathijs.rnoud.net/got/doutdes_extendsstuff.png
Op deze manier is het enige nadeel wat ik nog ondervind dat ik 2 maal iElement aan het implementeren ben, maar voor de rest kan ik mijn code recyclen. Aangezien ik de code vaak nodig ga hebben en het in de klasse zelf uitwerken mij dus een erg grote hoeveelheid dubbele code op gaat leveren lijkt me dit zo snel de mooiste oplossing.

Of is er iets wat ik nu over het hoofd aan het zien ben en kan ik het nog beter/simpeler oplossen?

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

M.I. kan voor ambiguiteit zorgen o.a. te zien in het diamand probleem. PHP heeft dit door single inheritance 'aangepakt, en als compromis multiple inheritance in interfaces toegelaten.
Dit wil nog niet zeggen dat je er niet mee kan wat jij wil, zij het via een omwegje. Je zult met aggregatie en delegatie je dubbele implementatie iig kunnen tegengaan.

  • Mac_Cain13
  • Registratie: Juni 2003
  • Laatst online: 20-05 01:14
prototype schreef op donderdag 16 november 2006 @ 01:12:
M.I. kan voor ambiguiteit zorgen o.a. te zien in het diamand probleem. PHP heeft dit door single inheritance 'aangepakt, en als compromis multiple inheritance in interfaces toegelaten.
Ik heb inderdaad over dit probleem gelezen tijdens mijn zoektocht naar een oplossing en begrijp ook goed waarom de mensen die de taal ontwikkelen deze keuze hebben gemaakt.
Dit wil nog niet zeggen dat je er niet mee kan wat jij wil, zij het via een omwegje. Je zult met aggregatie en delegatie je dubbele implementatie iig kunnen tegengaan.
Hmm, okay. Dus als ik je goed begrijp zeg je dat een dubbele implementatie van mijn code nooit nodig is. (In ieder geval niet in deze situatie.) Dat zou natuurlijk mooi zijn, want dubbele code is alleen maar verwarrend en slecht onderhoudbaar.

Misschien kun je (of iemand anders) me een beetje op weg helpen, want aggregatie en delegatie zijn mooie termen en theorieen alleen heb ik er niet zoveel aan als ik niet weet hoe ik ze nu moet toepassen. ;)
Het diagram wat ik hierboven post zorgt al voor werkende code, maar heeft wel nog een dubbele implementatie van de interface iElement (zowel Element als ContainerElement implementeren deze nu). Waaraan moet ik gaan denken als ik dit wil verbeteren? Of moet ik een totaal andere invalshoek gebruiken?

  • prototype
  • Registratie: Juni 2001
  • Niet online

prototype

Cheer Bear

Misschien heb ik implementeren ongelukkig gekozen als woord. Mijn punt is dat je de interfaces WEL kan implementen maar de code die de interfaces echt implementeert i.e. voorziet van concrete code kan aggregeren en dmv delegatie dubbele code tegen te gaan.
Wat je dus dan zou kunnen doen is zowel dus ContainerElement en Element, iElement laten implementeren op deze manier:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface iElement {
    public function foobar();
}

abstract class ContainerElement implements iElement {
    private $element;

    public function __construct()
    {
        $this->element = new Element();
    }

    public function foobar()
    {
        $this->element->foobar();
    }
}

public class Element implements iElement { 
    public function foobar() { //de daadwerkelijke implementatie
    }
}


ContainerElement aggregeert nu dus Element (sterker nog, het is composiet hier), implementeert ook iElement net als Element, maar delegeert de functioncall naar z'n geaggregeerde element. Kijk eventueel ook even naar de decorator pattern als ik denk wat je wil doen ;)

[ Voor 3% gewijzigd door prototype op 16-11-2006 02:20 ]

Pagina: 1