Objecten doorgeven, zo min mogelijk afhankelijk
Ik zit me af te vragen hoe ik op een transparante manier object het best kan doorgeven. Echter wil ik er voor zorgen dat de klassen niet afhankelijk van mekaar worden. Ik denk dat ik daar momenteel vrij goed in slaag. Alleen, ik ben er nog niet geheel uit. Ik zoek dus meningen, en eventuele alternatieven.
Ik ben bezig met het ontwikkelen van een klein simpel framework. Het framework is opgebouwd volgens het MVC principe. Alle requests komen binnen op mijn 'FrontController'. Deze bepaalt dan welke end controller er gebruikt moet worden, welke actie er uit gevoerd moet worden, paramaters...
Om de resources netjes te gebruiken, heb ik in mijn FrontController gebruik gemaakt van een Service Container. Deze houdt o.a. de log-service bij en de database-service. Deze worden dan overgedragen naar de uiteindelijke controllers, dus volgens mij zit dat goed qua afhankelijkheden en resources.
Verder heb ik ook gebruik gemaakt van interfaces. Bijvoorbeeld, de klasse database verwacht een parameter (via de constructor) ConfigInterface, zodanig dat ik vrij ben wat soort config object ik nu eigenlijk meegeef. Zolang het maar de ConfigInterface implementeert. Hierdoor creëer ik volgens mij voldoende flexibiliteit.
Ik vraag me af of dit dus een nette manier is.
ja, dat is het. :) (veel meer valt hier niet op te reageren naar mijn mening)
En ik vermoed dan dat de volgende opzet ook degelijk is:
Ik heb een abstract klasse, die alle controllers (behalve de front controller) uitbreiden. In deze abstracte controller, heb ik ook weer een service container. Deze houdt momenteel een service bij om formulieren te verwerken en om template-functies uit te voeren.
Nee, als het goed is heeft je hele applicatie toegang tot 1 en dezelfde service container waar dus alle services van de hele applicatie instaan. Dit geeft je de mogelijkheid het framework te kunnen aanpassen in de applicatie code.
Is het dan bv een goed idee om de service container, die dus in de front controller wordt gemaakt, door te geven? Met doorgeven bedoel ik dan, dat de abstracte controller deze krijgt. In de abstracte controller kan ik dan alsnog extra services aan de container toevoegen.
Met het eerste deel van je reactie (doorgeven van container) ben ik het wel eens. Totdat je van je controller services gaat maken, sommige devs houden daarvan, dan mag je alleen weer de services injecteren die je in de controller nodig hebt. Je moet je namelijk aan de regel houden: Als een klasse een service is mag het alleen andere services krijgen, niet de hele container. In elk ander geval moet de klasse juist de hele container (of niks) krijgen.
Ok, bedankt :-)
Dus alle mogelijke services zitten op voorhand in een config bestand? Tijdens het lopen van de applicatie kunnen er geen services worden toegevoegd? Dat lijkt met niet echt flexibel?
Waarom zou je je services willen aanpassen 'on run time'?
Oke dus voor je je app runt moet je alle mogelijk servises van alle mogelijke scenarios in een xml bestand of een ander config bestand hebben staan?
Ja, of je gebruikt ServiceProviders klasses die de services instellen. Of je gebruikt ze allebei :)
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
database
class = \PDO
host = localhost
usernaam = root
ww = wachtwoord
database = db1
storage
class = Framework\Blabla\PDOStorage
db = database
...
class = \PDO
host = localhost
usernaam = root
ww = wachtwoord
database = db1
storage
class = Framework\Blabla\PDOStorage
db = database
...
horen alle mapper hier ook in? dus:
En waar hoort dit bestand? In de app map of in de framework map?
Gewijzigd op 06/02/2013 10:56:01 door Jasper DS
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php
try {
$this->container = new ServiceContainer();
$this->container->set('logger.class', 'DebugLog');
//$this->container->set('logger.file', $_SERVER['DOCUMENT_ROOT'] . '/error_log');
$this->container->set('logger', function($c) {
$class = $c->get('logger.class');
//return new $class($c->get('logger.file'));
return new $class();
}, true);
$this->logger = $this->container->get('logger');
$this->logger->error('<h1>Debuglogger</h1>');
} catch (Exception $e) {
echo 'fatal error';
}
try {
$this->container->set('configparser.class', 'JsonParser');
$this->container->set('configparser.file', $this->getAbsolutePath() . '/config.json');
$this->container->set('configparser', function($c) {
$class = $c->get('configparser.class');
return new $class($c->get('configparser.file'));
}); //not needed to share
$this->container->set('config.class', 'Config');
$this->container->set('config', function($c) {
$class = $c->get('config.class');
return new $class($c->get('configparser'));
});
$this->container->set('database.class', 'Database');
$this->container->set('database.config', $this->container->get('config'));
$this->container->set('database.logger', $this->container->get('logger'));
$this->container->set('database', function($c) {
$class = $c->get('database.class');
return new $class($c->get('database.config'));
}, true);
$this->container->set('form.class', 'Form');
$this->container->set('form', function($c) {
$class = $c->get('form.class');
return new $class();
}, true);
$this->container->set('template.class', 'Template');
$this->container->set('template.layout', 'default');
$this->container->set('template', function($c) {
$class = $c->get('template.class');
return new $class($c->get('template.layout'), $c->get('form'));
});
$this->parseUri();
} catch (Exception $e) {
$this->logger->critical($e->getMessage());
}
?>
try {
$this->container = new ServiceContainer();
$this->container->set('logger.class', 'DebugLog');
//$this->container->set('logger.file', $_SERVER['DOCUMENT_ROOT'] . '/error_log');
$this->container->set('logger', function($c) {
$class = $c->get('logger.class');
//return new $class($c->get('logger.file'));
return new $class();
}, true);
$this->logger = $this->container->get('logger');
$this->logger->error('<h1>Debuglogger</h1>');
} catch (Exception $e) {
echo 'fatal error';
}
try {
$this->container->set('configparser.class', 'JsonParser');
$this->container->set('configparser.file', $this->getAbsolutePath() . '/config.json');
$this->container->set('configparser', function($c) {
$class = $c->get('configparser.class');
return new $class($c->get('configparser.file'));
}); //not needed to share
$this->container->set('config.class', 'Config');
$this->container->set('config', function($c) {
$class = $c->get('config.class');
return new $class($c->get('configparser'));
});
$this->container->set('database.class', 'Database');
$this->container->set('database.config', $this->container->get('config'));
$this->container->set('database.logger', $this->container->get('logger'));
$this->container->set('database', function($c) {
$class = $c->get('database.class');
return new $class($c->get('database.config'));
}, true);
$this->container->set('form.class', 'Form');
$this->container->set('form', function($c) {
$class = $c->get('form.class');
return new $class();
}, true);
$this->container->set('template.class', 'Template');
$this->container->set('template.layout', 'default');
$this->container->set('template', function($c) {
$class = $c->get('template.class');
return new $class($c->get('template.layout'), $c->get('form'));
});
$this->parseUri();
} catch (Exception $e) {
$this->logger->critical($e->getMessage());
}
?>
Gewijzigd op 06/02/2013 11:36:27 door Write Down
Dit stukje code staat inderdaad in een ander bestand :-). Maat technisch gezien, staat het dus wel in mijn front controller. Dat je het opsplitst in meerdere bestanden, is naar mijn mening eerder voor de duidelijkheid. Doe je dat niet, dan is dat ook geen ramp, alleen minder netjes.
Oke daar ben ik het met eens. Ik ga mijn config waarschijnlijk uit een .yml lezen (ben er nog mee aan het experimenteren) misschien is dat ook een optie?
Dat is een keuze dat je maakt natuurlijk. Zelf ga ik voor Json. Leesbaar & compact formaat. Verder wil ik ook delen van mijn applicatie uitwisselbaar maken, en dan is Json ook welkom. Verder een koud kunstje om van json naar XML te gaan :-)
Hoe ik zo'n configuratie bestand eruit zou laten zien en hoe je dat moet parsen heb ik zojuist in andere topics verteld: http://www.phphulp.nl/php/forum/topic/dependency-injection/89060/#639305 (vanaf deze reactie en verder)
Wat je ook kan doen is ServiceProviders maken. Een voorbeeldje daarvan kun je zien in Silex of in een applicatie van mij: Inspector. Hier zie je dat de providers worden aangeroepen, hoe deze providers eruit zien kun je hier bekijken.
Of zie ik dat helemaal verkeerd?