Door
jan terhuijzen
op 29-11-2013 22:10
gewijzigd op 29-11-2013 22:14
1.384 views
Hallo,
Bij OOP doe je wel eens een object meegeven aan een ander object.
Bijvoorbeeld:
<?php
class A {
private $_b;
public function __construct(B $objVanB) {
$this->_b = $objVanB;
}
public function doeIetsMetB() {
$this->_b->doeIets();
}
}
class B {
}
?>
Is dit goed gebruik van OOP?
Hierdoor worden klasses wel afhankelijk van elkaar, dus wil je klassen gaan hergebruiken, dan moet je de andere klassen mee nemen, omdat de ene klasse de andere nodig heeft.
Het komt vaak voor dat een class A class B nodig heeft, dus daar is op zich niks vreemds aan. Je moet wel proberen om het allemaal zo onafhankelijk mogelijk te programmeren en te zorgen dat je het niet te strict maakt.
Stel dat jouw class A een fabriek voorstelt die auto's in elkaar zet, dan zou het prettig zijn als deze fabriek niet alleen een auto/class B(MW) in elkaar kan zetten, maar ook overweg kan met een class A(lpha Romeo) en class C(itroën). Dus dat is wel iets om telkens goed over na te denken.
Ja, het is heel erg goed om andere klassen in andere klassen te gebruiken. Hierdoor weet je dat je de verantwoordelijkheden goed hebt verdeelt. Deze manier van injecteren van klassen heet Dependency Injection, een erg populair begrip in de PHP wereld tegenwoordig.
Er is echter 1 basis principe van OO die je goed in de gaten moet houden: Programmeer naar een interface, geen implementatie. Dit betekend dat je in jouw geval gelimiteerd bent tot alleen object B. Dat wil je niet, stel je hebt straks een andere implementatie van B die je C noemt, dan werkt het al niet.
Daarom maak je een interface. Een interface is een contract in OO. In een interface zeg je: Zodra een klasse een interface implementeert heb je toegang tot deze functies Stel we hebben een CacherInterface met een method fetch en save, dan weten we zeker dat wanneer de klasse dat interface implementeert we toegang hebben tot die methods. Zodra je dus alleen die methods gebruikt in je klasse ben je niet meer afhankelijk van die specifieke implementatie (bijv. FileCacher), maar je bent afhankelijk geworden van een interface (CacherInterface). Hierdoor open je de deur voor elke cacher implementatie.
Wil je nou nog wat door gaan in dit onderwerp ga je kijken naar een dependency injection container, ook wel service container genoemd. Dit is een klasse waarin je aangeeft hoe een service gemaakt moet worden. Een service is een klasse die een andere klasse injecteert (A in jouw geval) of een klasse die een andere nodig kan hebben (B in jouw geval).
De dependency injection container weet dus precies hoe een klasse gemaakt moet worden, jij hoeft dan alleen nog maar die klasse op te vragen:
<?php
$container = new Pimple(); // zie https://github.com/fabpot/pimple
// registreer hoe je de router.cacher service maakt
$container['router.cacher'] = function ($c) {
return new RoutingFileCacher();
};
// en de router
$container['router'] = function ($c) {
return new Router($c['router.cacher']); // de Router injecteert de cacher implementatie
};
class Router
{
private $cacher;
public function __construct(CacherInterface $cacher)
{
$this->cacher = $cacher;
}
// ...
public function getRoutes()
{
$routes = $this->cacher->fetch('routes'); // door het contract van het interface weten we dat deze method beschikbaar is
if (!$routes) {
$routes = ...; // load de routes
$this->cacher->save('routes', $routes);
}
Het komt vaak voor dat een class A class B nodig heeft, dus daar is op zich niks vreemds aan. Je moet wel proberen om het allemaal zo onafhankelijk mogelijk te programmeren en te zorgen dat je het niet te strict maakt.
Stel dat jouw class A een fabriek voorstelt die auto's in elkaar zet, dan zou het prettig zijn als deze fabriek niet alleen een auto/class B(MW) in elkaar kan zetten, maar ook overweg kan met een class A(lpha Romeo) en class C(itroën). Dus dat is wel iets om telkens goed over na te denken.
Vind je?
<?php
class carFactory {
private $car;
public function __construct($carbrand) {
$this->car = $carbrand;
}
}
?>
Dit kan in PHP, maar heeft in mijn ogen helemaal niets met OOP te maken.
Je maakt hier een object dat niet van te voren weet wat zijn verantwoordelijkheid is.
Die verantwoordelijkheid kun je afdwingen met een interface. Het ging me erom dat je niet te strict programmeert en daarmee het gebruik van je classes beperkt. Zie ook het voorbeeld van Wouter.
Het was maar een voorbeeldje Ger om duidelijk te maken dat je je niet teveel moet beperken. Die fabriek moet weten dat ie bij iedere auto 4 banden een stuur, stoelen, een motor en carosserrie in elkaar moet zetten. Het zou jammer zijn als die geweldige fabriek uitsluitend een Opel Astra 1.2 in elkaar kan zetten en verder niets. Het zou leuk zijn als ie ook andere modellen in elkaar kan zetten. Het was gewoon een voorbeeldje.
Hoe werkt dat precies met interfaces als parameters doorgeven? Ik kom er sinds 2 dagen achter dat je een interface kunt doorgeven als een parameter.
Wat gebeurt er dan?
Je slaat dan een interface op als property van een object, maar met een interface kun je toch niks doen? Want het is gewoon een lijst met methods.