Dependency Injection

De­pen­dency In­jec­tion (DI) for me al­ways used to be this mouth­ful of a term I read about in wordy and bor­ing Java en­ter­prise ar­ti­cles. The last cou­ple of months I ig­nored my bad as­so­ci­a­tion with DI and in­ves­ti­gated it in the form of Dag­ger for An­droid ap­pli­ca­tions. After the ini­tial learn­ing curve I even found use­ful po­ten­tial ap­pli­ca­tions of DI for our code base at work. So in this wordy and bor­ing ar­ti­cle I pre­sent my very own find­ings about DI. More pre­cisely, I em­pha­size some as­pects of DI that I found many ar­ti­cles gloss­ing over too much, i.e. the mo­ti­va­tion for DI frame­works, DI’s ap­pli­ca­tions apart from test­ing and DI’s trade-offs, and I touch upon the re­la­tion­ship be­tween DI and cer­tain other pro­gram­ming lan­guage con­cepts.

Overview

As it turns out, DI it­self is a big word for a sim­ple idea. To put it very bluntly for Java: “Make every ob­ject in a class a con­struc­tor pa­ra­me­ter”. A more del­i­cate ex­pla­na­tion goes some­thing like this. The ini­tial, non-DI sit­u­a­tion is that of a class C with local and fixed de­pen­den­cies that can­not be ex­changed from out­side. In such a sit­u­a­tion cou­pling be­tween C and its de­pen­den­cies is strong and the abil­ity to change these de­pen­den­cies for an in­stance of C by a client (aside from di­rect ma­nip­u­la­tion of the source code) is prac­ti­cally im­pos­si­ble. To make C now ad­here to the DI-prin­ci­ple these fixed de­pen­den­cies can sim­ply be pa­ram­e­trized into con­struc­tor pa­ra­me­ters re­sult­ing in C’. Cou­pling is re­duced a lot as C’ now only de­pends on the in­ter­faces of its de­pen­den­cies but not on their con­crete im­ple­men­ta­tions (hope­fully). Con­versely, change­abil­ity or flex­i­bil­ity of C’ is in­creased. 1

Let us give our, so far quite fea­ture­less, non-DI class C and DI class C’ each a face and il­lus­trate the point of DI with a con­crete, al­beit ar­ti­fi­cial, ex­am­ple.

class C {
   D d;
   C() { d = new D(); }
}

class C {
   D d;
   C(D dependency) { d = dependency; }
}

Let D1 and D2 be two new classes that each in­herit from D. We now see the ad­van­tage of DI. Namely, C’ can read­ily be in­stan­ti­ated with an in­stance of D1 in one place and an in­stance of D2 in an­other. The same is not true for C. 2

class D1 extends D { /* ... */ }
class D2 extends D { /* ... */ }
// ...
C c1 = new C(new D1());
// ...
C c2 = new C(new D2());

DI sounds great, does it not? There is a prob­lem though. Let us imag­ine that C’ has many more con­struc­tor pa­ra­me­ters. Its con­struc­tor ar­gu­ments need to be man­u­ally and ex­plic­itly ini­tial­ized, con­fig­ured and passed on for each con­struc­tion site (at least in pure Java). Worse yet, if all our classes fol­low the DI-prin­ci­ple there is a “tran­si­tive ini­tial­iza­tion ex­plo­sion”. Be­cause un­der­stand­ing this point is cru­cial, let me stress it with a con­crete, al­though again ar­ti­fi­cial, ex­am­ple. 3

We fol­low the DI prin­ci­ple and now face the task of cre­at­ing an in­stance x of the class X whose con­struc­tor merely has four de­pen­den­cies.

X x = new X(x1, x2, x3, x4);

But we still need to ini­tial­ize x1, x2, x3 and x4. Let us begin with x4.

X4 x4 = new X4(x41, x42);
X x = new X(x1, x2, x3, x4);

But we still need to ini­tial­ize x4’s de­pen­den­cies be­fore, i.e. in the cor­rect order.

// ...
X3 x3 = new X3(x31, x32, x33);
X42 x42 = new X42();
X41 x41 = new X41();
X4 x4 = new X4(x41, x42);
X x = new X(x1, x2, x3, x4);

Clearly, this me­chan­i­cal ac­count­ing is a great re­duc­tion in pro­gram­ming com­fort and at some point prob­a­bly un­sus­tain­able. 4 5

DI frame­works, as op­posed to the mere con­cept be­hind DI, mainly exist for just the pur­pose to make it pos­si­ble to ad­here to DI and thus profit from in­creased change­abil­ity with­out mak­ing a great com­pro­mise in pro­gram­ming com­fort or con­ve­nience. They take the bur­den of the me­chan­i­cal ac­count­ing from the pro­gram­mer. They are nei­ther evil nor some un­jus­ti­fied, over-en­gi­neered crap—their ex­is­tence is rea­son­ably mo­ti­vated. Granted, a big up­front learn­ing ef­fort is re­quired to get going with a DI frame­work and like every prac­ti­cal frame­work most of them are not per­fect and must make many com­pro­mises. These ini­tial costs and pos­si­ble deficits are more than amor­tized in a suf­fi­ciently com­plex pro­ject though.

A side mark about un­der­stand­ing DI. I per­son­ally never found that the Wikipedia ar­ti­cle, Mar­tin Fowler’s ar­ti­cle or even the main re­sults for a “De­pen­dency In­jec­tion” Google search re­ally made it click for me what all the fuss about DI is. What helped me not only un­der­stand the DI prin­ci­ple—which is rel­a­tively straight-for­ward—but rather its con­se­quences are the fol­low­ing video pre­sen­ta­tions. 6

Fla­vors

One of the main ad­van­tages which is touted in many DI ar­ti­cles is that DI makes for eas­ier test­ing. Other touted ad­van­tages may in­clude: com­fort of sta­tic vari­ables with­out guilty con­science, bet­ter pre­pared to in­cor­po­rate changes (e.g. re­plac­ing data­base soft­ware), real mod­u­lar­iza­tion and bet­ter ad­her­ing to the sin­gle re­spon­si­bil­ity prin­ci­ple. These are all great ad­van­tages and much has been writ­ten about them so I do not want to re­peat what you can find eas­ily else­where. In­stead, I want to em­pha­size a ben­e­fit that di­rectly fol­lows from the in­creased change­abil­ity DI in­tro­duces: the im­proved abil­ity to cre­ate dif­fer­ent fla­vors from a com­mon code base. If every­thing is ex­change­able then it is easy to re­place a cer­tain part to cre­ate a de­sired cus­tomiza­tion. 7 8 9

How come this as­pect of DI in­ter­ests me? You see, at work we face the prob­lem to main­tain dif­fer­ent, cus­tomized ver­sions of the same soft­ware for mul­ti­ple clients. Our cur­rent so­lu­tion is roughly and sim­pli­fied the fol­low­ing. We have a base repos­i­tory which is the un­cos­tu­mized fla­vor. Each cus­tomized pro­ject is a fork of the base repos­i­tory and re­ceives its re­spec­tive cus­tom changes. Every cou­ple of days the base repos­i­tory is merged into the cus­tomized repos­i­tory so that it re­ceives im­prove­ments and bug fixes from the base. Cercer­illa and Htbaa from the above thread es­sen­tially ex­plain our so­lu­tion. We do not use this sys­tem very long yet but from what I can tell it works al­right. But to be hon­est it irks me a bit be­cause I know there is a “bet­ter” so­lu­tion to our prob­lem and it in­volves DI.

I will just quote Alb’s an­swer be­cause he ex­plains this DI-ap­proach well.

Don’t do this with SCM branches. Make the com­mon code a sep­a­rate pro­ject that pro­duces a li­brary or pro­ject skele­ton ar­ti­fact. Each cus­tomer pro­ject is a sep­a­rate pro­ject which then de­pends on the com­mon one as a de­pen­dency. This is eas­i­est if your com­mon base app uses a de­pen­dency in­jec­tion frame­work like Spring, so that you can eas­ily in­ject dif­fer­ent re­place­ment vari­ants of ob­jects in each cus­tomer pro­ject as cus­tom fea­tures are re­quired. Even if you don’t al­ready have a DI frame­work, adding one and doing it this way may be your least painful choice.

But why ex­actly is the DI-ap­proach with the com­mon code base as a li­brary “su­pe­rior” to SCM deltas? The linked thread gives some po­ten­tial an­swers, for ex­am­ple quick­ly_now‘s ob­jec­tion that “you still have to main­tain all those deltas in source con­trol and keep them lined up—for­ever. Short term this might work. Long term it might be ter­ri­ble”. How­ever, I be­lieve the main dis­ad­van­tage of the branch-based ap­proach com­pared to the DI-li­brarized ap­proach is that the for­mer loses vital struc­tural in­for­ma­tion be­cause it works on the ab­strac­tion level of text and not on the richer syn­tax and se­man­tics of the em­ployed pro­gram­ming lan­guage. By using DI and (in­ter­face) in­her­i­tance there are more use­ful con­straints to be obeyed to and which pre­vent cer­tain classes of mis­takes. Look­ing at this point from an­other per­spec­tive one could also say that this li­brary-based ap­proach forces one much more to con­sider how the li­brary is going to be used which hope­fully will lead to a more use­ful de­sign. 10 11 12

The DI-based li­brary ap­proach is sadly not a sil­ver-bul­let ei­ther be­cause of “log­i­cal” con­flicts or rather back­wards-in­com­pat­i­ble changes. For ex­am­ple, iden­ti­fier re­nam­ings, changes to pre-/post-con­di­tions or in­vari­ants and sig­na­ture changes in the li­brary may make cer­tain fla­vors stop work­ing cor­rectly after an up­date even if there were not any com­piler er­rors. The golden ap­proach—which is taken by many open-source pro­jects—to mit­i­gate these “dan­ger­ously silent” changes is to have a changelog in order to pre­pare clients of the li­brary of pos­si­ble prob­lems and sim­ply fol­low­ing a cau­tious way of in­tro­duc­ing these changes by not just re­mov­ing old APIs or dras­ti­cally chang­ing their be­hav­ior but in­stead mark­ing them as dep­re­cated and in­tro­duc­ing al­ter­na­tive, new APIs. Still, I be­lieve it is the best cur­rent so­lu­tion to pro­vide dif­fer­ent cus­tomer fla­vors from a com­mon code base in gen­eral—for each spe­cific sit­u­a­tion you have to weigh in all the as­pects, of course, and other so­lu­tions might be a bet­ter deal then. 13

Trade-offs

Fol­low­ing DI does not au­to­mat­i­cally mean you should pa­ram­e­trize every sin­gle de­pen­dency of a class. Only do this if you feel you gain some­thing out of it. Of course, feel­ing and know­ing are worlds apart so it is a tough call to make. De­cid­ing on trade-offs is never easy but it is a big part of being a de­vel­oper.

Re­struc­tur­ing a big, ac­tively main­tained non-DI code base to fol­low DI prin­ci­ples is an enor­mous un­der­tak­ing. The up­front learn­ing curve needed to get pro­fi­cient with a DI frame­work is quite big as well. There is more scaf­fold­ing and prob­a­bly a bit more boil­er­plate needed for a pro­ject. These (ini­tial) costs should never be un­der­es­ti­mated and de­pend­ing on the scope and the longevity of a pro­ject they might never pay off.

The in­creased change­abil­ity that is gained with DI is payed for with less sta­bil­ity or rather pre­dictabil­ity. As the im­ple­men­tor of a DI-based li­brary you give up con­trol. This is a fun­da­men­tal trade-off that is also re­lated to en­cap­su­la­tion. To learn more about this trade-off sim­ply search Google for “de­pen­dency in­jec­tion en­cap­su­la­tion” and you will surely dis­cover this re­mark­able ar­ti­cle. You have to pose your­self the ques­tion what is more im­por­tant to you, change­abil­ity or sta­bil­ity. I would say that for the vast ma­jor­ity of (end-user) soft­ware pro­jects the abil­ity to quickly adapt to changed re­quire­ments is worth more than sta­bil­ity.

Re­la­tion to Other Con­cepts

In this sec­tion I merely slightly touch upon DI’s re­la­tion­ship to other com­puter sci­ence, pro­gram­ming or pro­gram­ming lan­guage con­cepts as a more thor­ough trea­tise would go be­yond the scope of this ar­ti­cle. I find this topic very in­ter­est­ing though, so I hope to pub­lish a thor­ough ar­ti­cle about it in the fu­ture.

  • We have seen in the pre­vi­ous sec­tion that there is a fun­da­men­tal ten­sion be­tween en­cap­su­la­tion and DI.
  • Us­ages of DI in­ter­sect with po­ten­tial us­ages of global vari­ables, sin­gle­tons or sta­tic fields and dy­namic scop­ing.
  • In DI frame­works there is the con­cept of mod­ules which are es­sen­tially fac­to­ries. In gen­eral, DI and DI frame­works are re­lated to the sta­tic and ab­stract fac­tory pat­tern and the ser­vice lo­cater pat­tern.
  • The fol­low­ing is a quote by Gilad Bracha from his ar­ti­cle Con­struc­tors Con­sid­ered Harm­ful: “DI frame­works are just a work around for a de­fi­ciency in the un­der­ly­ing lan­guage.” This state­ment is very in­ter­est­ing and nat­u­rally raises the ques­tion what na­tive so­lu­tions other non-main­stream lan­guages have to offer to tackle the same prob­lem that DI frame­works tackle. For ex­am­ple, in Scala there is the cake pat­tern.

Con­clu­sion

In this ar­ti­cle we have learned that there is a dif­fer­ence be­tween the idea of DI and DI frame­works: namely con­ve­nience. The idea of DI is sim­ple. Yet its con­se­quences are ex­ten­sive. Among other use­ful ap­pli­ca­tions hav­ing the abil­ity to cre­ate cus­tomized fla­vors from a com­mon code base is one of DI’s par­tic­u­lar strengths. Even though DI has many ben­e­fits there are trade-offs to be made and de­pend­ing on the spe­cific sit­u­a­tion DI might not be worth it—but it very often is. DI in­ter­sects other pro­gram­ming re­lated con­cepts and the need to use DI frame­works can be seen as a “de­fi­ciency in the un­der­ly­ing lan­guage”. 14


  1. In a more ab­stract sense, DI means adding an­other layer of in­di­rec­tion to your “en­closed en­tity”. This layer con­tains links to other en­ti­ties and can be con­fig­ured in­di­vid­u­ally for each en­tity which ul­ti­mately re­sults in greater change­abil­ity or flex­i­bil­ity of the soft­ware. The con­cept of “ad­di­tional in­di­rec­tion to gain flex­i­bil­ity” is preva­lent in com­puter sci­ence, for ex­am­ple in the form of point­ers, higher-or­der func­tions, call­backs, dy­namic dis­patch and fac­tory meth­ods. On the other hand, more flex­i­bil­ity is in gen­eral paid for with less sta­bil­ity (or rather pre­dictabil­ity and often per­for­mance, too). But for most prac­ti­cal soft­ware pro­jects change­abil­ity is worth much more than sta­bil­ity. As an aside, I in­vented the term “en­closed en­tity” on the spot sim­ply to make it clear that I am not nec­es­sar­ily talk­ing about ob­jects in the OOP sense. 

  2. One could maybe come up with the doomed idea to re­place C’s con­struc­tor body with d = new D1(). How­ever, all in­stances of C would then be ini­tial­ized with D1 and it still would not be pos­si­ble to choose the de­pen­dency for each in­stance of C

  3. This “tran­si­tive ini­tial­iza­tion ex­plo­sion” is re­lated to the phe­nom­ena “com­bi­na­to­r­ial ex­plo­sion”

  4. This prob­lem ex­ists mainly due to the tran­si­tiv­ity of the de­pen­den­cies which can be con­cep­tu­al­ized as a big di­rected acyclic graph (DAG). As an aside, DAG is the in­spi­ra­tion for the name Dagger. In one of the linked videos the pre­sen­ter says that in a Google An­droid pro­ject there were 3000 lines of code just for this ac­count­ing and in a back­end pro­ject there were around 100000 lines of code be­fore the switch to a DI frame­work… 

  5. Some peo­ple ap­par­ently have no prob­lem with the re­duced pro­gram­ming com­fort and gladly trade this pain for the pain of using a DI frame­work: http://​www.​yegor256.​com/​2014/​10/​03/​di-containers-are-evil.​html#​the-right-way Per­son­ally, I would al­ways choose the DI frame­work as I con­sider its pain much smaller. 

  6. I must admit that I read a lot about DI be­fore it fi­nally made sense to me so these videos might not be enough to make it click for some­one to­tally new to DI. Also, I played with Dag­ger in prac­tice and for­mu­lated ques­tions that I put on e.g. Stack­over­flow so that helped me, too. On the other hand, I am a slow un­der­stander any­way. Your mileage may vary. 

  7. You may some­times get the im­pres­sion that DI is only use­ful for “ser­vices”. E.g. the abil­ity to ex­change one data­base provider with an­other in your soft­ware. But this is “just” a spe­cial case of DI. Nev­er­the­less, as I will ex­plain fur­ther in the sec­tion “Trade-offs”, using DI only for ser­vices and noth­ing more might be the sweet-spot for DI or, put dif­fer­ently, the “best” trade-off be­tween change­abil­ity and sta­bil­ity for a soft­ware pro­ject. 

  8. I am using “fla­vors” for lack of a bet­ter term. I guess I could have also used the terms types, vari­ants, ver­sions, forms, con­fig­u­ra­tions or man­i­fes­ta­tions. I hope it is still clear what I mean by fla­vors. 

  9. The testa­bil­ity ad­van­tage can be un­der­stood from this fla­vor per­spec­tive. Namely, one is able to pro­vide a spe­cial fla­vor of the com­mon code base which sim­ply has the char­ac­ter­is­tic that it is suit­able for test­ing (“mock-mode”). 

  10. In prac­tice it might not even be su­pe­rior be­cause of jus­ti­fied time- and re­source-based counter ar­gu­ments. Even just re­struc­tur­ing the ap­pli­ca­tion after DI and hav­ing de­vel­op­ers in­vest a great learn­ing ef­fort into DI and a DI frame­work might not pay off in the short- and mid-term and pos­si­bly not even in the long-term if the dif­fer­ences be­tween the fla­vors are rather shal­low. 

  11. Also, merge con­flicts when merg­ing the base pro­ject into the cus­tomized pro­ject sim­ply will not hap­pen with the li­brary-based ap­proach be­cause this ac­tiv­ity it­self sim­ply van­ishes. 

  12. What about not using DI and in­her­i­tance but in­stead one big pro­ject with if state­ments lit­tered through the code base of the form if (isCustomer1) then doCustomer1Stuff()? Well, it cer­tainly works on the ab­strac­tion level of the lan­guage’s syn­tax and se­man­tics. But it has many dis­ad­van­tages. For ex­am­ple the code of a class/method/state­ment body mixes many dif­fer­ent con­cerns and be­comes hard to fol­low. So this ap­proach should not be used. Note that pre-proces­sor if-defs work on the ab­strac­tion level of text and so are “even worse” but cer­tainly good enough for non-com­plex tasks. 

  13. Also, there is a ten­sion with en­cap­su­la­tion, the gran­u­lar­ity of classes and DI. Be­cause the more ex­posed and smaller your classes are the eas­ier it is to in­ject just a small new class into the base li­brary to achieve a de­sired change. If the classes are big­ger or have many pri­vate meth­ods you may have to recre­ate (or copy&paste) much func­tion­al­ity be­fore you can in­ject. But en­cap­su­la­tion is good, right? Learn more in the next sec­tion. 

  14. Much more can be said about DI. A his­tor­i­cal analy­sis would be in­ter­est­ing for ex­am­ple. Even though I tried to keep scope of ar­ti­cle small it still be­came rel­a­tively large. I hope I could shed some light on some oth­er­wise weakly il­lu­mi­nated points about DI

Published: 26.01.2015
Comment? Mail or tweet me.