[DDD] Dependency Injection vs Service Locator

Pagina: 1
Acties:
  • 199 views sinds 30-01-2008
  • Reageer

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Ik zit me af te vragen in welke gevallen je opteert voor 'dependency injection', en in welke gevallen je dan weer kiest voor een 'service locator'.
Zijn hier bepaalde 'guidelines' voor, is het puur kwestie van smaak, wat zijn de voordelen / nadelen van de ene aanpak tov de andere ?

Stel, je hebt een class 'Customer'.
Die class Customer heeft een property 'Status'. Die property geeft weer of de klant een 'Gold Customer' , een 'Normal Customer', of een 'Bad Payer' is.
Om dit na te gaan, heb je een aantal gegevens nodig uit de database; bv, om na te gaan of de klant een 'Gold Customer' is, moet je checken of de klant in de laatste 3 maanden minstens voor 2000 euro spullen besteld heeft; als hij daarentegen meer dan 3 facturen op 5 te laat betaald heeft, dan is het een bad payer.
Om dit na te gaan , heb je dus in de Customer class een 'InvoiceRepository' nodig.
De vraag is nu; hoe ga je die InvoiceRepository aan die customer class gaan aanleveren ?
Ga je dit doen mbhv 'Dependency Injection' (bv een instance van een InvoiceRepository aanleveren aan het Customer object mbhv de constructor of een setter), bv:
code:
1
2
Customer c = customerRepository.GetCustomer (5);
c.InvoiceRepository = repositoryFactory.GetInvoiceRepository();


of, ga je mbhv een 'Service Locator' binnen die Status property de correcte / gewenste IInvoiceRepository gaan ophalen ?
code:
1
2
3
4
5
6
7
8
9
10
public CustomerStatus Status
{
    get
    {
        IInvoiceRepository invRep = DomainSettings.Instance.RepositoryLocator.GetInvoiceRepository();

        ...
        
    }
}


Dus, in welke gevallen kies je voor 'injection', en wanneer kies je voor 'locators' ?

https://fgheysels.github.io/


Verwijderd

Weinig verschil, alleen heeft DI een pluspuntje mbt schaalbaarheid en SL mbt logisch ontwerpen:

Bij DI kan van buitenaf (public) het type InvoiceRepository gezet en veranderd worden. Dit biedt voor ontwikkelaars mogelijkheden om de betreffende classes te hergebruiken in andere Libraries en applicaties (in tegenstelling tot het SL pattern).

een SL haalt zijn informatie op binnen die bepaalde klasse en kan niet snel ingezet worden in andere applicaties, echter is het volgens de OOP principes wel logischer om SL te gebruiken, aangezien het de verantwoordelijkheid van de betreffende klasse is, de juiste service erbij te zoeken.

Kwestie van ontwerp eisen en smaak dus.

  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
De mooiste oplossing vind ik dat het automatisch gebeurd, in java heb je daar bv Spring voor. Icm AspectJ kan je prototype beans dergelijke klassen (repositories) automatisch gaan injecteren bij het aanmaken van een Customer.

Java:
1
Customer c = new Customer(); //de repository is automatisch geinjecteerd


Voordeel is dat je de Client niet verantwoordelijk stelt of er wel degelijk een repository aanwezig is. Stel ook dat je via een ORM mapper een 100 tal Customers ophaalt, dan zal jij er eerst volledig moeten over itereren om er overal een repository te injecteren.

De manier van service locator is een oplossing, maar dan zit je in je Customer klassen met een dependency naar je DomeinSettings als je ooit de implementatie van repository wil wijzigen moet je dan je lookup functie in code gaan wijzigen om de juist repository terug te geven, via dependency injection is het kwestie van configureren

"Live as if you were to die tomorrow. Learn as if you were to live forever"


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

In mijn optiek is het wel een fundamenteel verschil. Dependency injection is doorgaans lastiger te programmeren dan een rechttoe-rechtaan ServiceLocator. Dat nadeel ben je vrijwel helemaal kwijt als je iets als Spring, PicoContainer of Avalon inzet.

Naar mijn idee is dependency injection helemaal anders dan een ServiceLocator. De ServiceLocator is een design pattern, dependency injection is een designstroming/architectuurprincipe. De ServiceLocator zet je in om het opzoeken/initialiseren van lastige resources (in J2EE bijv: JNDI) voor minder bekwame developers te vergemakkelijken en je code niet bomvol try catch blokken te hoeven zetten. DI zet je niet zomaar even in.

En het voordeel van dependency injection is simpelweg dat het totaal niet intrusive is en je domein lekker schoon blijft. Een ServiceLocator blijft altijd expliciet in je code staan.

Fat Pizza's pizza, they are big and they are cheezy


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 08:32:
De mooiste oplossing vind ik dat het automatisch gebeurd, in java heb je daar bv Spring voor. Icm AspectJ kan je prototype beans dergelijke klassen (repositories) automatisch gaan injecteren bij het aanmaken van een Customer.
Sorry, maar dat is gewoon ronduit smerig. Het is natuurlijk niet de bedoeling dat je alle logica gaat verstoppen. Enige referentie naar daadwerkelijke code is gewoon fijn en scheelt simpelweg een hoop tijd. Dat is wat mij betreft dan ook gelijk het grote voordeel van een SL, je ziet in de class waar de afhankelijkheden vandaan komen. En ja, dan lever je een beetje flexibiliteit in die je met DI wel hebt. Het is dus denk ik geen kwestie van wanneer de een beter is dan de ander, maar eigenlijk enkel wat je smaak is en de mate van flexibiliteit die je wilt hebben.

  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
Verwijderd schreef op dinsdag 19 juni 2007 @ 08:49:
[...]
Sorry, maar dat is gewoon ronduit smerig.
Ik vind dit niet smerig, je kan perfect in je klasse (via Annotations) en in het Spring configuratie bestand zien welke dependency geinjecteerd wordt! Ik vind het pas smerig worden wanneer je manueel telkens een repository moet gaan injecteren via een setter!
Het is natuurlijk niet de bedoeling dat je alle logica gaat verstoppen. Enige referentie naar daadwerkelijke code is gewoon fijn en scheelt simpelweg een hoop tijd. Dat is wat mij betreft dan ook gelijk het grote voordeel van een SL, je ziet in de class waar de afhankelijkheden vandaan komen. En ja, dan lever je een beetje flexibiliteit in die je met DI wel hebt. Het is dus denk ik geen kwestie van wanneer de een beter is dan de ander, maar eigenlijk enkel wat je smaak is en de mate van flexibiliteit die je wilt hebben.
SL vind ik zoals JVKA zei meer toepasselijk bij het ophalen van resources uit bv een JNDI directory

"Live as if you were to die tomorrow. Learn as if you were to live forever"


  • NetForce1
  • Registratie: November 2001
  • Laatst online: 23:17

NetForce1

(inspiratie == 0) -> true

Een combinatie is ook mogelijk. Je kunt gerelateerde services in een service locator (of context object) opnemen en vervolgens die service locator (laten) injecteren.

De wereld ligt aan je voeten. Je moet alleen diep genoeg willen bukken...
"Wie geen fouten maakt maakt meestal niets!"


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 09:00:
Ik vind dit niet smerig, je kan perfect in je klasse (via Annotations) en in het Spring configuratie bestand zien welke dependency geinjecteerd wordt! Ik vind het pas smerig worden wanneer je manueel telkens een repository moet gaan injecteren via een setter!
En dat is nou precies mijn punt, het wordt een via via weg. je moet dus waar je de class aanmaakt
- de class induiken
- opzoek naar de annotatie
- opzoek naar de juiste xml
- de juiste bean def erbij
- de concrete dependency in
- en dan er achter komen dat er weer een annotatie in de depency staat.

Nee, we hebben niets te verbergen hoor ;) Dergelijke oplossingen met allemaal automagische annotatie meuk maken projecten enkel nodeloos complexer. En nee ik ben alles behalve anti spring/annotatie/aspect/whatever, maar je dient het wel met mate te gebruiken. Niet simpelweg omdat het kan.
Cuball schreef op dinsdag 19 juni 2007 @ 09:00:
SL vind ik zoals JVKA zei meer toepasselijk bij het ophalen van resources uit bv een JNDI directory
Wat maakt een JNDI resource anders dan een willekeurige andere resource vanuit het dependency management oogpunt?

  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
Verwijderd schreef op dinsdag 19 juni 2007 @ 09:12:
[...]

En dat is nou precies mijn punt, het wordt een via via weg. je moet dus waar je de class aanmaakt
- de class induiken
- opzoek naar de annotatie
- opzoek naar de juiste xml
- de juiste bean def erbij
- de concrete dependency in
- en dan er achter komen dat er weer een annotatie in de depency staat.
ik vind dat je het hier toch te omslachtig voorstelt hoor, alleen al door in je Spring configuratie bestand kijken kan je al direct zien welk dependencies in je klasse geinjecteerd worden.

XML:
1
2
3
    <bean  class="Customer" scope="prototype">
        <property name="invoiceRepository" ref="invoiceRepository"/>
    </bean>


de annotations heb je enkel nodig om aan te geven dat deze klasse geconfigureerd wordt door Spring. Alles kan je terugvinden in je XML bestand. Met alle flexibiliteit die je er gratis bijkrijgt!
Als je via SL werkt moet je toch ook in je klasse gaan kijken, dan kijken welke service die locator nu effectief terug geeft, ...! Bij DI vind je alles terug in het XML configuratie bestaand, mooi bijeen...
[...]
Wat maakt een JNDI resource anders dan een willekeurige andere resource vanuit het dependency management oogpunt?
Niets, als wij een JNDI resource gebruiken dan gebruiken we ook DI, maar ik kan er gewoon beter inkomen dat dergelijke (niet zo veel voorkomende resources) via een SL opgehaald worden

"Live as if you were to die tomorrow. Learn as if you were to live forever"


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 09:33:
ik vind dat je het hier toch te omslachtig voorstelt hoor
Ik ben bang van niet:
code:
1
new Customer(invoiceRepository);
Keep it simple, is gewoon veel beter te volgen en je levert nauwelijks tot niets aan flexibiliteit in.
Cuball schreef op dinsdag 19 juni 2007 @ 09:33:
maar ik kan er gewoon beter inkomen dat dergelijke (niet zo veel voorkomende resources) via een SL opgehaald wordt
Ja maar juist naar die motivatie ben ik benieuwd. Dat is juist voer voor dit topic :)

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Location is idd wellicht netter vanuit het 'client' oogpunt. Die hoeft zich nl. niets aan te trekken van het al dan niet setten / geven van een dependency aan de entity.
Echter, wat als je dependency op zich weer een dependency nodig heeft ?

In het voorbeeld van de topicstart bv: Stel dat de IInvoiceRepository een (N)Hibernate ISession object nodig heeft. Is het dan mogelijk om dit probleem met een service locator op te lossen ?
In bepaalde gevallen zal je bv een reeds bestaande ISession moeten doorgeven (bv voor Lazy Load issues in (N)Hibernate). In dit geval kan je -imho- beter de weg van 'injection' kiezen, niet ?

https://fgheysels.github.io/


  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
Verwijderd schreef op dinsdag 19 juni 2007 @ 09:38:
[...]

Ik ben bang van niet:
code:
1
new Customer(invoiceRepository);
Keep it simple, is gewoon veel beter te volgen en je levert nauwelijks tot niets aan flexibiliteit in.
hoe ga je dan om met objecten die aangemaakt zijn via een ORM mapper ? daar kan je niet zomaar aan de constructor je repository gaan meegeven

"Live as if you were to die tomorrow. Learn as if you were to live forever"


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 09:49:
hoe ga je dan om met objecten die aangemaakt zijn via een ORM mapper ? daar kan je niet zomaar aan de constructor je repository gaan meegeven
Vind het sowieso fout als je die functionaliteit nodig hebt. Maar ik kan me dan ook compleet niet vinden in DDD. Maar dit gaat teveel offtopic...

  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

Wat ik bedoelde met mijn JNDI opmerking was vooral dat voordat DI bekend was, er al JNDI was en het veel mensen afschrikte. Al die contexten en namingexceptions en zo. Om dat een beetje toegankelijker te maken hebben ze daar een pattern voor geintroduceerd, zodat die 'lastige' details verborgen gehouden konden worden.

Eigenlijk dus een fix voor een vervelende API. Tegenwoordig heeft ServiceLocator een ander main doeleinde gevonden, namelijk het abstraheren van service lookup. (was altijd al een consequentie van het pattern)

Toen kwam er een slimme meneer met het idee om de boel om te draaien met DI, wat het ServiceLocator pattern een beetje vervangen heeft.

Een beetje vergelijkbaar met Service Activator en MDB's.

Fat Pizza's pizza, they are big and they are cheezy


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Cuball schreef op dinsdag 19 juni 2007 @ 09:49:
[...]

hoe ga je dan om met objecten die aangemaakt zijn via een ORM mapper ? daar kan je niet zomaar aan de constructor je repository gaan meegeven
DI hoeft niet altijd via de Constructor te verlopen; je hebt ook 'setter injection'.

https://fgheysels.github.io/


  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
whoami schreef op dinsdag 19 juni 2007 @ 10:03:
[...]

DI hoeft niet altijd via de Constructor te verlopen; je hebt ook 'setter injection'.
Ja dat weet ik, maar ik wou hier verwijzen naar mark platvoet zijn opmerking, dat hij manueel zijn dependency in z'n domein object steekt en dit niet automatisch laat gebeuren door een DI-container icm AspjectJ.

Op deze manier kan je moeilijk objecten die je ORM mapper aanmaakt automatisch gaan setten met de juiste dependency. Als je dan toch manueel via een setter werkt dan moet je zoals eerder vermeld over alle opbjecten die je bv uit een dao terug krijgt gaan iteren om er de dependencies in te steken.

"Live as if you were to die tomorrow. Learn as if you were to live forever"


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 10:11:
Ja dat weet ik, maar ik wou hier verwijzen naar mark platvoet zijn opmerking, dat hij manueel zijn dependency in z'n domein object steekt en dit niet automatisch laat gebeuren door een DI-container icm AspjectJ.
Ik steek helemaal niets manueel mijn domein object in, ik steek manueel een dependency een object in. En tuurlijk, voor elke aanpak is een uitzondering te verzinnen waarin die aanpak minder of zelfs niet werkt. Je aanpak zal in die specifieke situatie misschien wel een van de betere zijn. Een SL in de constructor van je class zou een oplossing kunnen zijn.

Ik vind een laagdrempelig project een van de belangrijkste argumenten in je technische keuzes. Code moet leesbaar en begrijpelijk blijven. Elke vorm van dependency management zorgt automatisch voor een abstractie laag en verhoogt daarmee de drempel. Waarmee ik dus niet willen zeggen dat je ten alle tijde lagen moet vermijden, maar als je zonder kan dan moet je dat gewoon doen.

Ik vind dus een SL meer leesbaar/laagdrempeliger dan DI. Ik denk tevens dat het beter in de (misplaatste) gedachtegang van DDD past. Immers met DDD leg je de verantwoordelijkheid bij je domein, dus je domein dient ook zijn dependencies binnen te harken.

  • Cuball
  • Registratie: Mei 2002
  • Laatst online: 28-11 14:44
Verwijderd schreef op dinsdag 19 juni 2007 @ 11:15:
[...]
Ik vind dus een SL meer leesbaar/laagdrempeliger dan DI. Ik denk tevens dat het beter in de (misplaatste) gedachtegang van DDD past. Immers met DDD leg je de verantwoordelijkheid bij je domein, dus je domein dient ook zijn dependencies binnen te harken.
offtopic:
wat bedoel je met "misplaatste" gedachtengang ?


Ik heb zo het gevoel dat jij uit een standpunt vertrek met oog op leesbaarheid en het sneller kunnen begrijpen van code voor personen die geen deel uitmaken van een project. Maar ik vind dit persoonlijk teniet gedaan door een goed gedocumenteerde architectuur.

"Live as if you were to die tomorrow. Learn as if you were to live forever"


Verwijderd

Cuball schreef op dinsdag 19 juni 2007 @ 11:29:
offtopic:
wat bedoel je met "misplaatste" gedachtengang ?


Ik heb zo het gevoel dat jij uit een standpunt vertrek met oog op leesbaarheid en het sneller kunnen begrijpen van code voor personen die geen deel uitmaken van een project. Maar ik vind dit persoonlijk teniet gedaan door een goed gedocumenteerde architectuur.
Ja en nee. Ja een goed gedocumenteerde architectuur haalt inderdaad veel van het leed weg. Daar kan ik het niet anders dan mee eens zijn. Nee, ik merk zelf op projecten die wat langer lopen (of bugfixes) en wat groter zijn in omvang dat het terug vinden van specifieke code moeilijker wordt. Zelfs als ik het zelf heb geschreven (of de architectuur heb opgezet for that matter). Dat zegt documentatie uiteraard wel eits, maar lang niet alles.

offtopic:
DDD zou niet mogen betekenen dat logica/verantwoordelijkheden meteen maar verhuist moeten worden. Designen vanuit het domein model is goed, dat is de klant, dat is de business. Maar ik vind het misplaatst dat het een complete shift in logica/verantwoordelijkheid teweeg brengt. Het geeft je meer problemen(/uitdagingen) dan dat het oplossingen biedt. Zo heb je weer een adapter laag naar je presentatie laag en dat is feitelijk extra werk

  • EfBe
  • Registratie: Januari 2000
  • Niet online
whoami schreef op maandag 18 juni 2007 @ 20:41:
Ik zit me af te vragen in welke gevallen je opteert voor 'dependency injection', en in welke gevallen je dan weer kiest voor een 'service locator'.
Zijn hier bepaalde 'guidelines' voor, is het puur kwestie van smaak, wat zijn de voordelen / nadelen van de ene aanpak tov de andere ?
In de DDD group op Yahoo! groups is dit al zo vaak tot in den treure kapot bediscussieerd en de consensus is dat er 2 groepen zijn: de een zegt: "A domain class shouldn't know about repositories" en de ander zegt "Why not?". De groepen staan tegenoverelkaar als Xbox vs Playstation fanboys en dat maakt de status van de discussie een beetje duidelijk hoop ik. Eric Evans komt dan af en toe tussenbeide met nietszeggende opmerkingen als "it depends on the project" en meer van die zalvende woorden waar je niets concreets aan hebt.

Dat gezegd hebbende, laat ik mn eigen ongezouten mening eens geven ;)
Stel, je hebt een class 'Customer'.
Die class Customer heeft een property 'Status'. Die property geeft weer of de klant een 'Gold Customer' , een 'Normal Customer', of een 'Bad Payer' is.
Om dit na te gaan, heb je een aantal gegevens nodig uit de database; bv, om na te gaan of de klant een 'Gold Customer' is, moet je checken of de klant in de laatste 3 maanden minstens voor 2000 euro spullen besteld heeft; als hij daarentegen meer dan 3 facturen op 5 te laat betaald heeft, dan is het een bad payer.
Om dit na te gaan , heb je dus in de Customer class een 'InvoiceRepository' nodig.
De vraag is nu; hoe ga je die InvoiceRepository aan die customer class gaan aanleveren ?
Ga je dit doen mbhv 'Dependency Injection' (bv een instance van een InvoiceRepository aanleveren aan het Customer object mbhv de constructor of een setter), bv:
code:
1
2
Customer c = customerRepository.GetCustomer (5);
c.InvoiceRepository = repositoryFactory.GetInvoiceRepository();


of, ga je mbhv een 'Service Locator' binnen die Status property de correcte / gewenste IInvoiceRepository gaan ophalen ?
code:
1
2
3
4
5
6
7
8
9
10
public CustomerStatus Status
{
    get
    {
        IInvoiceRepository invRep = DomainSettings.Instance.RepositoryLocator.GetInvoiceRepository();

        ...
       
    }
}


Dus, in welke gevallen kies je voor 'injection', en wanneer kies je voor 'locators' ?
Lood om oud ijzer.

Dependency Injection (DI) is niets anders dan de implementatie van het IoC principe: de class zelf creeert geen instanties van gerelateerde objects, dat wordt gedaan door een extern object. Dit kan bv gedaan worden door een Factory in een IoC container die instances injecteert (en dus buiten het object wordt aangeroepen), een locator of een DI provider die door de CTor van de class wordt aangeroepen en dan de objects injecteert. Het punt is gewoon: je hebt een class A en een instance daarvan krijgt instanties van andere classes, welke weet je niet, geinjecteerd at runtime. Je legt dus niet hardcoded vast welke objects er in de instanties van A worden geplaatst at runtime. That's it. Meer is er gewoon niet. Ene meneer Fowler mag dan wel beweren dat het meer is of anders is, maar dat is de zg. Fowler-eske draai die hij aan alle bekende dingen geeft: het lijkt net of hij het bedacht heeft en de definitie zo is zoals hij het definieert. Tja.

Ik moet eerlijk zeggen dat ik nooit begrijp waarom mensen dingen als 'Gold customer' in de customer class willen bouwen. Je ontkomt nl. niet aan een dependency op order, indirect of niet. (en een DI config regel om een order repository te injecten in customer is ook een dependency)

BL die een enkel veld (ID > 0) of meerdere velden binnen dezelfde entity (shipping date >= order date), volkomen duidelijk, maar cross-entity logica, ik zou het niet in de customer plaatsen. Het punt is nl, je code zakt IMHO dan weg in een moeras van dependencies: iedere entity heeft dan wel een of meerdere dependencies en wat als je 2 of 3 hops verder moet voor je logica? Dat wordt een aardig web.

Ik zou dan ook een service maken (Of om de Fowler-isten even tegen de haren in te strijken: een Manager class ;)) die dit voor je oplost, dus die cross-entity logica voor je verzorgt. Dit levert nl. een centraal punt op waar deze logica wordt geplaatst, wat IMHO veel onderhoudbaarder is.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Pagina: 1