Afhankelijke objecten
Bij OOP doe je wel eens een object meegeven aan een ander object.
Bijvoorbeeld:
Code (php)
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.
Gewijzigd op 29/11/2013 22:14:06 door Jan terhuijzen
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.
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:
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
30
31
32
33
34
35
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
30
31
32
33
34
35
<?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);
}
return $routes;
}
}
?>
$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);
}
return $routes;
}
}
?>
http://nl.wikipedia.org/wiki/Aspectgeoriënteerd_programmeren ), het gebruik hiervan lijkt mij beter/mooier met wat jij wilt bereiken.
Kijk ook eens naar AOP ( Gewijzigd op 30/11/2013 20:07:27 door Local Dev
Ozzie PHP op 29/11/2013 23:36:06:
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.
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?
Code (php)
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.
Gewijzigd op 30/11/2013 09:59:29 door Ger van Steenderen
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.
public function __construct(CacherInterface $cacher)
Maar jij zegt in de laatste alinea dat een class(fabriek) met verschillende typen objecten(automerken) overweg moet kunnen.
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.
Het zou helemaal niet jammer zijn als de fabriek uitsluitend Opel Astra's kan maken, sterker nog, dat is precies wat er in de echte wereld gebeurd.
Ik hoop toch dat ie ook een Astra 1.4 1.6 enz. in elkaar kan zetten.
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.
Met het geen je voor een parameter zet geef je aan welk type class het moet zijn. Bijvoorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dit voorbeeld werkt met exacte klassen, je kan ook met child klassen en dus ook met interfaces type hinten:
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
30
31
32
33
34
35
36
37
38
39
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
30
31
32
33
34
35
36
37
38
39
<?php
class RouteLoader
{
public function __construct(CacherInterface $cacher)
{
// ...
}
}
interface CacherInterface
{
public function fetch($id);
public function save($id, $data);
}
class FileCacher implements CacherInterface
{
// ...
}
class DatabaseCacher implements CacherInterface
{
// ...
}
class VerkeerdeCacher //! let op, implementeert niet CacherInterface
{
// ...
}
$cacher = new FileCacher(...);
$loader = new RouteLoader($cacher); // werkt, 1e argument implementeert CacherInterface
$cacher = new DatabaseCacher(...);
$loader = new RouteLoader($cacher); // werkt ook, 1e argument implementeert weer CacherInterface
$cacher = new VerkeerdeCacher(...);
$loader = new RouteLoader($cacher); // werkt niet, 1e argument implementeert niet CacherInterface
?>
class RouteLoader
{
public function __construct(CacherInterface $cacher)
{
// ...
}
}
interface CacherInterface
{
public function fetch($id);
public function save($id, $data);
}
class FileCacher implements CacherInterface
{
// ...
}
class DatabaseCacher implements CacherInterface
{
// ...
}
class VerkeerdeCacher //! let op, implementeert niet CacherInterface
{
// ...
}
$cacher = new FileCacher(...);
$loader = new RouteLoader($cacher); // werkt, 1e argument implementeert CacherInterface
$cacher = new DatabaseCacher(...);
$loader = new RouteLoader($cacher); // werkt ook, 1e argument implementeert weer CacherInterface
$cacher = new VerkeerdeCacher(...);
$loader = new RouteLoader($cacher); // werkt niet, 1e argument implementeert niet CacherInterface
?>
Ik heb DI nooit echt gebruikt moet ik zeggen maar is dit niet iets wat je in principe zou kunnen gebruiken ipv een singleton?
1 voor heel je applicatie.
> Ik heb DI nooit echt gebruikt moet ik zeggen maar is dit niet iets wat je in principe zou kunnen gebruiken ipv een singleton?
Ja, behalve dat je singletons vanuit een object gebruikt en je bij een service container juist ervoor zorgt dat het object niet weet welke klasse hij krijgt. Maar wil je meer weten, maak even een nieuw topic :)