Ola,

Op advies van enkelen van jullie leek het me een goed idee om van iedere service een apart object te maken wat je vervolgens toevoegt aan een container. Dus we hebben zeg maar een service met wat instellingen en die voegen we toe aan een container.

<?php
$container->add(new Service($instellingen, 'foo'));
$container->add(new Service($instellingen, 'bar'));
?>
Als ik dan een service nodig heb, doe ik:

<?php
$foo = $container->get('foo');
?>
Omdat ik met losse Service objecten werk, weten die niet van elkaars bestaan af. En daar komt mijn "probleem". Stel nu dat de service 'foo' gebruik maakt van de service 'bar', hoe krijg ik dat dan voor elkaar?

Het lijkt mij dan dat het service object 'foo' toegang moet hebben tot de container waar het object in zit. Houdt dit dan in dat ik op het moment dat ik een service object toevoeg aan de container, ik de container als parameter moet meegeven aan het service object, dus zoiets:

<?php
$container->add(new Service($instellingen, 'foo', $container));
?>
Of is er een betere oplossing waardoor de service objecten gebruik kunnen maken van elkaar?
Ik vind zelf de oplossing van Pimple mooi en simpel: services worden gedefinieerd met simpele factories in de vorm van een anonymous function. Deze krijgt dan als argument de container zelf mee, waardoor je zo beschikking hebt over al je afhankelijke services of parameters.

<?php
$container['session_storage'] = function ($c) {
return new SessionStorage('SESSION_ID');
};

$container['session'] = function ($c) {
return new Session($c['session_storage']);
};
?>

Wat ik anders zou doen is het singleton maken van services (maar 1x instantiëren en daarna hetzelfde object teruggeven) default maken. Pimple doet dat zo:
<?php
$container['session'] = $container->factory(function ($c) {
return new Session($c['session_storage']);
});
?>
Zie ook: https://github.com/fabpot/Pimple#defining-services

Als je liever niet met anonymous functions werkt maar met de objecten zoals je in je voorbeeld gebruikt. Moet je misschien afstappen van de constructor gebruiken in je services en e.o.a. setup() functie in je interface definiëren, die als argument de container meekrijgt.
Thanks Pim, leuk om je weer eens te zien hier :)

Ik ben inderdaad niet zo van de anonymous functions. In de "oude" situatie sloeg ik alle gegevens die ik nodig had om een service te maken op in een array binnen de container. Omdat alles in de container zat, konden de services onderling met elkaar "praten". Als ik van iedere service een los object maak, wat ik vervolgens aan de container toevoeg, dan is dat weliswaar een mooiere oplossing, maar de services kunnen niet meer met elkaar communiceren. Mijn idee was dat een service-object z'n eigen service kon teruggeven aan de container. Dus, stel ik heb een service-object A, dan vraag je dat via de container op, en het service-object geeft de service dan aan de container, en de container geeft het weer aan mij. Maar je hebt dan een probleem als service-object A een ander service-object (B) nodig heeft, want A en B kunnen niet bij elkaar komen.

Dit zou dus betekenen dat service-objecten niet hun eigen service kunnen aanmaken (want dat gaat blijkbaar niet werken) maar dat ze uitsluitend de instellingen van een service bevatten. De container, of een aparte processor, zou dan vervolgens een service moeten samenstellen. Maar dat betekent dus wel dat een service-object uitsluitend configuratie-gegevens vasthoudt en verder helemaal niks uitvoert. Is dat niet raar?
>> Is dat niet raar?

Nee, DTO's, Entities, ValueObjects. Allemaal voorbeelden van objecten die alleen data vasthouden. Als je een duidelijke separatie wilt hebben tussen je implementation en domain layer ga je heel veel van dit soort objecten gebruiken.
Hmmmm, oké... thanks. Ik moet nog even wennen aan het idee dat je dit in losse objecten stopt in plaats van in 1 grote array :) Maar als jij zegt dat dit de goede manier is, dan doen we het zo!

Reageren