container sharen
In de tutorial over dependency injection wordt gesproken over het sharen/delen van services. Je kunt een service zo instellen dat telkens dezelde service (object) wordt teruggegeven, maar je kunt een service ook zo instellen dat telkens een nieuwe service wordt terugggeven. In de eerste situatie als ik Foo opvraag en ergens anders in de code nogmaals Foo opvraag, krijg ik dezelfde Foo terug. In de laatste situatie als ik Foo opvraag en ergens anders in de code nogmaals Foo opvraag, krijg ik een nieuwe Foo terug. Nu vraag ik me af wanneer je dat laatste, het telkens creëren van een nieuwe service (object), in de praktijk gebruikt. Waarom zou je dat eigenlijk willen?
Als voorbeeld voor een request, je wilt niet iedere keer een nieuwe request aanmaken maar die eenzelfde request terugkrijgen die je als eerst instelde.
Voorbeeld: ik heb een class die met sets van coordinaten (op een kaart) kan werken. Het kan er berekeningen op uitvoeren als afstanden berekenen etc. Deze class is zo opgebouwd dat het gebruik maakt van de data die is opgeslagen in de class. Als ik nu twee sets van coordinaten tegelijk wil verwerken, dan krijg ik dus problemen op het moment dat ik deze class vanuit mijn service container teruggeef in telkens hetzelfde object. Bij elke aanroep geef ik dus een nieuwe instantie terug.
Voorbeeld van een service waarvan je hetzelfde object terug kan geven zou een request class zijn, zoals Raoul al aangeeft. Of bijvoorbeeld een class die mails kan versturen. Zodra de mail verstuurd is is de data niet meer nodig.
Wanneer dat handig is? Bij stateless classes. Bijv. een DataGrid class die een tabel maakt. Je voegt daar data in en die data wordt dan opgeslagen in de klasse. Als je telkens dezelfde instance zal krijgen betekend dat dus dat je maar 1 keer je DataGrid class kunt gebruiken; dat wil je niet!
Zoals jij het schetst moet je in die datagrid telkens nieuwe (dynamische) data invoeren. Waarom is dit dan een service? Dan zou je toch ook gewoon kunnen zeggen:
$datagrid = new Datagrid()
Wat is dan precies de meerwaarde van een service? Ik dacht dat een service bedoeld was om een object te instantiëren met argumenten afkomstig uit een configuratiebestand. Maar in jouw voorbeeld kan dat niet omdat de input dynamisch is. Of zie ik nu iets over het hoofd?
Kan een service bijvoorbeeld ook zoiets zijn:
return new Datagrid();
... dus zonder argumenten?
En eventueel zou de Datagrid een Render klasse (bijv. TableRenderer) als dependency hebben.
Maaaar... dan kun je toch eigenlijk alles als service in die container proppen?
Dan zou ik ook een service 'user' kunnen maken, die een niet gevuld User object teruggeeft, dat ik dan vervolgens zelf moet vullen.
Dan krijg je dus dit:
$user = $this->container->get('user');
in plaats van
$user = new User();
Wat is dan precies de meerwaarde van zo'n service?
Van zo'n service niet. Een User is een Entity, een klasse dat data vast houdt. Dat is niet echt een service. Een DataGrid is een klasse die meer doet dan gegevens vasthouden en een dependency kan krijgen.
En het is dus niet per se noodzakelijk dat er sprake is van dependency?
Toevoeging op 10/03/2013 15:24:20:
Laat ik m'n vraag dan nog eens wat lastiger maken. Stel dat die datagrid geen dependency kent... is het dan nog steeds een service?
Want het enige verschil is dan de manier waarop je de datagrid aanroept:
$datagrid = $this->container->get('datagrid');
of
$datagrid = new Datagrid();
Met je tweede manier gooi je het grote voordeel van je service container het raam uit. Doordat je dat doet kan je 'nooit' meer een andere implementatie van het Datagrid gebruiken. Het voordeel van je service container is juist dat de rest van de applicatie alleen weet dat er een object zal worden aangemaakt dat bepaalde dingen kan, maar niet weet van welke klas het precies is. Je service container regelt dat en via de instellingen kan je bepalen welke klas precies geinstantieerd wordt. Dat kan per applicatie verschillen. Als je nu klassen gaat instantieren buiten je service container om dan raak je die flexibiliteit kwijt.
Maar... in dat geval, kun je dan niet veel beter ieder object in je service container stoppen?
Want dan zou je er toch ook een 'user' in kunnen stoppen? En stel in de ene applicatie gebruik ik dan de user class User() en in de andere bijvoorbeeld UserShopper(). Is dat een beetje wat je bedoelt?
Op een paar uitzonderingen na worden objecten bij mij altijd gemaakt in een factory of service container. Het is niet de taak van enig andere klas om een object aan te maken.
"...worden objecten bij mij altijd gemaakt in een factory of service container."
Wat bedoel je hier precies mee. Wat is het verschil tussen beiden?
Voor de view is er een view factory die de algemene view objecten kan aanmaken.
Overkoepelend (en voor de controller) heb ik een applicatie factory die de objecten kan aanmaken die de controller nodig heeft (inclusief de andere twee factories).
de factories hebben ook kennis van bijvoorbeeld welke database er moet worden gebruikt en dus welke adapter nodig is (op het moment overigens alleen MySQL, maar kan op zeker moment iets anders worden), of welke output taal er gebruikt moet worden (HTML4, HTML5, javascript etc.).
De service container is een algemeen object dat alle objecten standaard kunnen aanspreken. Op het moment dat een object aangemaakt wordt (en een bepaalde interface implementeert) dan krijgt dat object automatisch de service container geinjecteerd. Objecten die een bepaalde functionaliteit bieden die niet noodzakelijk te maken heeft met het MVC model worden via de service container aangemaakt. Denk bijvoorbeeld aan converters, mail objecten, http header object, coordinaten objecten etc.
Of dit allemaal strict de definities van factories en service containers aanhangt weet ik niet en boeit me niet heel erg om eerlijk te zijn. In mijn omgeving werkt dit naar wens en ik kom eigenlijk nooit in een situatie waarin het niet voldoet.
Gewijzigd op 10/03/2013 16:09:44 door Erwin H
Ik werk tot nu toe altijd gewoon met $iets = new Iets() dus je kunt je voorstellen dat zoiets als jij doet nogal een ommekeer is... heftig!