vrijdag 6 september 2013

Objecten binnen objecten van dezelfde PHP-klasse

Soms heb je bij het objectgeoriënteerd programmeren (OOP) in PHP een object nodig dat meerdere objecten van dezelfde klasse bevat. En soms moet dat meerdere niveaus diep, met objecten binnen objecten die op hun beurt nog meer objecten bevatten.

Eenvoudige PHP-klasse

In de natuur en de techniek komen onderdelen die onderdeel zijn van andere onderdelen veel voor. Denk bijvoorbeeld aan onderdelen van auto’s. Eén onderdeel kan één ander onderdeel bevatten. Een auto heeft bijvoorbeeld één stuurkolom met daarop één stuurwiel en daarin vervolgens één airbag. Eén onderdeel kan ook meerdere onderdelen bevatten. Een auto heeft vier wielen, elk autowiel heeft één velg en elke velg heeft vijf wielmoeren.

Dit kun je in PHP bijvoorbeeld als volgt modelleren. De klasse Part voor één onderdeel bevat de array $parts voor meerdere onderdelen. De methode addPart(Part $part) vereist één object van de eigen klasse Part en voegt dat toe aan de array $parts:

class Part
{
    private $parts = array();

    public function addPart(Part $part)
    {
        $this->parts[] = $part;
    }
}

Met deze PHP-klasse kunnen we bijvoorbeeld als volgt drie onderdelen maken en die in elkaar monteren:

// Drie onderdelen definiëren
$stuurkolom = new Part();
$stuurwiel  = new Part();
$airbag     = new Part();
// De airbag in het stuur monteren
$stuurwiel->addPart($airbag);
// Het stuur aan de stuurkolom monteren
$stuurkolom->addPart($stuurwiel);

Als we de structuur van het object $stuurkolom tonen met print_r($stuurkolom), dan krijgen we de volgende hiërarchie van geneste objecten in arrays:

Part Object
(
    [parts:Part:private] => Array
        (
            [0] => Part Object
                (
                    [parts:Part:private] => Array
                        (
                            [0] => Part Object
                                (
                                    [parts:Part:private] => Array
                                        (
                                        )
                                )
                        )
                )
        )
)

Uitgebreide PHP-klasse

De eerste versie van onze klasse Part heeft een beperking: we kunnen met de methode addPart() maar één onderdeel tegelijk toevoegen. Stel dat we één auto willen uitrusten met vier wielen. Dan moeten we vier wielen definiëren en daarvoor vier keer addPart() aanroepen:

// Eén auto definiëren
$auto = new Part();
// Vier autowielen definiëren en aan de auto monteren
for ($i = 1$i <= 4$i++) {
    $autowielen[$i] = new Part();
    $auto->addPart($autowielen[$i]);
}

In dit voorbeeld is $autowielen een array en zijn de array-elementen $autowielen[1] tot en met $autowielen[4] de vier wielen. Zouden we elk van de vier wielen willen uitrusten met één velg, dan moeten we zelfs acht keer de methode addPart() aanroepen:

$auto = new Part();
for ($i 1$i <= 4$i++) {
    // Een autowiel definiëren
    $autowielen[$i] = new Part();
    // Een velg definiëren
    $velgen[$i ]= new Part();
    // De velg in het wiel monteren
    $autowielen[$i]->addPart($velgen[$i]);
    // Het wiel aan de auto monteren
    $auto->addPart($autowielen[$i]);
}

Dit is omslachtig en onhandig. Bedenk bijvoorbeeld maar eens wat er gebeurt wanneer we elk van de vier autowielen moeten uitrusten met een autoband en elke velg met vijf velgmoeren. Dan krijgen we onleesbare PHP-code. Of maken we fouten door onderdelen te vergeten of een wielmoer in een autoband te monteren in plaats van aan een velg…

Een betere oplossing is een aparte methode toevoegen die in één keer een array met meerdere onderdelen accepteert. In het volgende voorbeeld is dat de nieuwe methode addParts():

class Part
{
    private $parts = array();

    public function addPart(Part $part)
    {
        $this->parts[] = $part;
        return $this;
    }

    public function addParts(array $parts)
    {
        foreach ($parts as $part) {
            $this->addPart($part);
        }
        return $this;
    }
}

Met deze verbetering kunnen we nu in één keer alle autowielen aan een auto monteren. Dat is logischer én overzichtelijker:

// Vier autowielen met elk één velg
for ($i = 1$i <= 4$i++) {
    // Een autowiel definiëren
    $autowielen[$i] = new Part();
    // Een velg definiëren
    $velgen[$i] = new Part();
    // De velg in het wiel monteren
    $autowielen[$i]->addPart($velgen[$i]);
}

// Alle wielen aan een auto monteren
$auto = new Part();
$auto->addParts($autowielen);

Zoals je in dit laatste voorbeeld ziet, heeft de verbeterde oplossing nog een groot voordeel voor objectgeoriënteerd programmeren. We kunnen autowielen en velgen definiëren zonder eerst een auto te definiëren. De autowielen zijn meer zelfstandige objecten geworden. We kunnen de montage van de wielen aan de auto uitstellen. En we kunnen het zelfs helemaal overslaan.

Geen opmerkingen:

Een reactie posten