OOP: APIE (encapsulation)

July 18th, 2012 | 4 min read | Coding rules, Object oriënted, PHP

De laatste peiler in APIE is encapsulation. Encapsulation is het inkapselen van jouw velden zodanig dat je er enkel via methodes aan kan.

Heads up!

Deze tutorial sluit aan bij mijn vorige tutorial in de reeks van OOP tutorials, APIE (inheritance).

In mijn vorige tutorials had ik uitgelegd dat er verschillende access modifiers zijn, namelijk: public, private en protected.
Stel dat we een class Mens hebben en we willen graag de naam van een instantie van de class Mens, dan kunnen we iets als hieronder schrijven:

<?php

class Mens {

        public $naam;

        public function __construct($naam) {
                $this->naam = $naam;
        }

}

$mens = new Mens("Dimitri");
echo $mens->naam;

?>

Omdat we $naam hier public gemaakt hebben, kunnen we er rechtstreeks aan. Probleem opgelost zou je denken, doch is dit in een groot deel van de gevallen niet goed.
Stel maar eens voor dat we hetzelfde zouden doen, maar dan voor de geboortedatum.

<?php

class Mens {

        public $geboorteDatum;

        public function __construct($datum) {
                $this->geboorteDatum = $datum;
        }

}

$mens = new Mens(new DateTime("1989-12-08"));
echo $mens->geboorteDatum->format('d-m-Y');

?>

Simpel zeg je, we geven gewoon een geboortedatum en klaar, het werkt. Maar weken later blijkt dat je graag ervoor wilt zorgen dat mensen niet zomaar een geboortedatum in de toekomst willen invoeren, hoe ga je nu die controle doorvoeren?
Laten we even kijken wat er zou gebeuren als we

$geboorteDatum

hadden encapsulated.

<?php

class Mens {

        private $geboorteDatum;

        public function __construct($datum) {
                $this->SetGeboorteDatum($datum);
        }

        public function SetGeboorteDatum($datum) {
                $this->geboorteDatum = $datum;
        }

        public function GetGeboorteDatum() {
                return $this->geboorteDatum;
        }
}

$mens = new Mens(new DateTime("1989-12-08"));
echo $mens->GetGeboorteDatum()->format('d-m-Y');

?>

Als we nu een controle willen toevoegen, dan hoeven we enkel de methode

SetGeboorteDatum()

aan te passen, terwijl we anders toch een methode gaan moeten aanmaken en ALLE classes die gebruik maken van jouw eens-o-zo-prachtige class Mens, nu allemaal hun code moeten aanpassen.
Het moraal van het verhaal is gewoon op voorhand al beginnen met alles te encapsuleren zodanig dat je achteraf niet met de problemen zit.

Exceptions

We hebben nu gezien hoe we controle kunnen invoeren op onze velden, de vraag is nu alleen; wat doen we als de gebruiker een foute datum invoert?
Voor foutafhandeling kunnen we het beste exceptions gebruiken. Er zijn 2 mogelijkheden indien er zich een exception voordoet:

  • Je vangt deze foutmelding op en doet er dus iets mee (foutmelding tonen ofzo), dit noemen we catch,
  • Je geeft deze door aan het object dat de methode opgeroepen heeft, dit noemen we throw.

Onze class Mens zou er na encapsulation en foutafhandeling zo uit zien:

<?php

class Mens {

        private $geboorteDatum;

        public function __construct($datum) {
                $this->SetGeboorteDatum($datum);
        }

        public function SetGeboorteDatum($datum) {
                if ($datum > new DateTime("now"))
                        throw new Exception("Geboortedatum mag niet in de toekomst liggen");
                else
                        $this->geboorteDatum = $datum;
        }

        public function GetGeboorteDatum() {
                return $this->geboorteDatum;
        }
}

try {
        $mens = new Mens(new DateTime("1989-12-08"));
        $mens2 = new Mens(new DateTime("2099-12-08"));
        echo "Mens 1: ". $mens->GetGeboorteDatum()->format('d-m-Y') . "<br />";
        echo "Mens 2: ". $mens2->GetGeboorteDatum()->format('d-m-Y') . "<br />";
} catch (Exception $exc) {
        echo "ERROR: ". $exc->GetMessage();
}

?>

In de methode

SetGeboorteDatum()

controleren we nu ofdat de geboortedatum in de toekomst ligt of niet, indien dat het geval is gaan we een exception throwen met een zelf gekozen bericht.
Als we een throw uitvoeren, zal deze naar een niveau hoger gebracht worden, naar het stuk code dat dus de methode oproept waar een exception zich voordeed. In ons geval is er geen methode meer, maar is dat ons main stuk code onderaan.

Daar gaan we de exception opvangen door middel van een

try .. catch ...

principe.
In het try block komt alle code waar exceptions kunnen voorkomen, terwijl het catch blok dan weer gebruikt wordt indien er een exception voorkomt.
Dit is in het voorbeeld het geval bij de initializatie van ons object$mens2.

We kunnen exceptions dus blijven throwen zo lang we maar willen, zo lang we deze maar ergens opvangen. Een voorbeeld waarbij we een exception meerdere keren verder throwen en zelfs een extra bericht toevoegt vind je hieronder:


In class A komt er een exception voor, in class B voegen we er een extra bericht aan toe en in class C throwen we deze exception nogmaals.

Back to tutorialsContact me on TwitterDiscuss on Twitter

Profile picture

Dimitri "g00glen00b" Mestdagh is a consultant at Cronos and tech lead at Aquafin. Usually you can find him trying out new libraries and technologies. Loves both Java and JavaScript.