Dependency Injection
- Summary
-
Discussion
- Could you explain dependency injection with an example?
- What are the benefits of using dependency injection?
- What are some potential problems of using dependency injection?
- What are the different types of dependency injection?
- Are there frameworks for dependency injection?
- Could you share some tips on dependency injection for beginners?
- Milestones
- References
- Further Reading
- Article Stats
- Cite As
The recommended practice in creating complex software is to adopt a modular design. Functionality is decomposed into modules or classes, each doing something specific and exposing their services via well-defined interfaces. This implies that one module will often depend on other modules or components of the system.
Dependencies among modules can lead to code that's tightly coupled and less maintainable. Dependency Injection (DI) is therefore used to resolve dependencies at runtime rather than at compile time. Objects that have dependencies will not themselves create those dependencies. They will instead rely on an another entity to create and inject those dependencies.
Dependency Injection is considered a design pattern and not a framework. It's one way of implementing a more general software concept called Inversion of Control (IoC). It's also part of SOLID Design Principles.
Discussion
-
Could you explain dependency injection with an example? Let's consider a class
XRateForecaster
that forecasts currency exchange rates. It accesses a database to obtain historical rates. It accesses a remote service for real-time rates. It also invokes an algorithm that does the forecasting. This class therefore has three dependencies that need to be fulfilled for it to work properly.Without DI,
XRateForecaster
will probably instantiate these dependencies on its own. This would work but what happens if these dependencies are modified in future?XRateForecaster
would need to be modified. Worse still,XRateForecaster
is tightly coupled with its dependencies. We can't do any unit testing with it without resolving its dependencies.With DI, the dependent class does not construct its dependencies. The dependencies are injected into it at runtime. Obviously, moving legacy code to this design pattern implies that code has to be refactored. It should be noted that DI doesn't remove dependencies. It merely separates the creation of dependencies from their consumption.
-
What are the benefits of using dependency injection? DI loosens the tight coupling between modules, classes or services. Code maintainability improves. Code can be reused. Because dependencies are injected at runtime, a different mock implementation can be injected for the purpose of unit testing. In fact, unit testing is one of the big benefits of adopting DI.
With DI, different implementations of the dependencies can be supplied. In other words, the dependent class can be written in abstraction without worrying about concrete implementations of its dependencies. Dependencies are no longer scattered across the app. They can be centralized in a container, or specified in an XML file.
Dependency carrying happens when a class instantiates a service it doesn't need but one of its dependencies needs. DI solves this problem. Jakob Jenkov describes many situations where DI is useful.
-
What are some potential problems of using dependency injection? Because dependencies are resolved at runtime, errors in dependencies cannot be caught at compile time. Tracing dependencies can be difficult. In languages such as Java or C#, there can be an explosion of data types. Programmers can become overdependent on DI frameworks. Learning a specific DI framework and managing the configuration can be extra work.
Using DI for the sake of using it is wrong. If you're never going to use another implementation or configuration, then it's best to avoid DI. One of the principles of object-oriented programming is encapsulation but DI break encapsulation. DI brings some of the implementation details, the dependencies, out to the interface.
DI can be done without containers. In fact, it's been said that containers add more lines of code and even more files if XML configuration is used. It adds unnecessary complexity. Worse still, using global singleton as injector causes problems.
-
What are the different types of dependency injection? There are three common ways of injecting dependencies:
- Constructor Injection: Dependency is passed to the object via its constructor that accepts an interface as an argument. A concrete class object is bound to the interface handle. This is typically used if the dependent object has to use the same concrete class for its lifetime.
- Method Injection: A.k.a. interface-based injection. Dependency is passed to the object via a method. This is useful if a different concrete object needs to be used at different times. For example, if the dependency is a notification, sometimes this may sent as an SMS, and other times as an email for the same dependent object.
- Property Injection: A.k.a. setter injection. If the dependency is selected and invoked at different places, we can set the dependency via a property exposed by the dependent object, which can then invoke it later.
An alternative to the above is Service Locator, which abstracts away the job of resolving dependencies. However, some identify this too as a form of dependency injection since the locator itself has to be injected into the dependent object.
-
Are there frameworks for dependency injection? While it's possible to implement dependency injection without any frameworks, what if your dependencies themselves depend on other dependencies? Frameworks simplify the job by providing what's called an IoC Container or Dependency Injection Container (DIC).
A container will create the dependent object along with its dependencies. Writing container code can also get tedious since we need to create one for each dependent class. This is where frameworks come in by providing generic DICs that can read in configuration information about dependencies, say, from an XML file.
Spring Framework, PicoContainer, HiveMind, XWork, Java EE6 CDI, Weld, Google Guice, Salta, Glassfish HK2, Dagger and Managed Extensibility Framework (MEF) are example DI frameworks. For .NET, DI frameworks include Spring.NET, Castle Windsor, Unity, StructureMap, Autofac and Ninject.
In Spring, there are at least three ways of doing DI: XML, annotations, pure Java code. Angular has its own DI mechanism using
@Injectable
decorator. ASP.NET Core does it via its built-in service container,IServiceProvider
Google Guice uses@Inject
constructor annotation. For Android, there's Dagger, RoboGuice, ButterKnife and Android Annotation. -
Could you share some tips on dependency injection for beginners? Passing in too many dependencies is an indication that the class is doing too much. It's better to split the class into multiple classes, each having a specific responsibility. Injecting dependencies but not using them or passing them to other classes is another anti-pattern. Inject interfaces, not implementations. Know what services need to be exposed, mark them public and keep all others private. If you use configuration files (XML, YAML, etc.) keep them readable.
A common anti-pattern is to invoke a singleton static container to instantiate services from within a dependent class. This is the service locator approach. It's using IoC container without doing DI. Instead, inject an interface into the constructor. If you use containers, register all components and resolve at the root component.
DI is not the only way to achieve IoC. Other ways include factory pattern, template method pattern, strategy pattern and service locator pattern. However, for example, with factory pattern the class still instantiates the factory and dependencies from within rather than being injected from the outside.
Milestones
1994
Robert Martin notes that object-oriented design on its own will not make code robust, maintainable and reusable. The pattern of interdependencies across subsystems matters too. A module's intent should not dependent on its details. He uses the term Dependency Inversion. In June 1995 he defines the Principle of Dependency Inversion,
Details should depend upon abstractions. Abstractions should not depend upon details.
Stefano Mazzocchi and others propose Java Apache Server Framework, later called Avalon. This uses IoC as one of its fundamental design principles. Avalon may be credited with popularizing IoC. According to Mazzocchi, IoC is really about isolation, modularity and reuse. The need to inject dependencies is an effect, not the cause.
Spring Framework is released. Spring was conceived by Rod Johnson a year earlier and released as part of his book on the framework. It uses DI and IoC containers, although he states that the term DI was coined in late 2003. Also in 2003, names type 1/2/3/4 are renamed to today's familiar names interface/setter/constructor injection.
References
- Aasenden, Jon L. 2015. "Inversion of control, dependency Injection, service oriented programming?" January 03. Accessed 2018-09-12.
- Allen, Rob. 2012. "What problem does dependency injection solve?" Rob Allen's DevNotes, February 20. Accessed 2018-09-10.
- Amit. 2015. "Spring Framework History: 2002 – Present." Spring Tutorials, December 26. Updated 2017-10-01. Accessed 2018-09-12.
- Angular Guide. 2018. "Introduction to services and dependency injection." Version 6.1.8-build.48914+sha.b9a5ce1, None. Accessed 2018-09-10.
- Bugayenko, Yegor. 2014. "Dependency Injection Containers are Code Polluters." October 03. Accessed 2018-09-10.
- Crusoveanu, Loredana. 2018. "Intro to Inversion of Control and Dependency Injection with Spring." Baeldung, April 19. Accessed 2018-09-10.
- Fayard, Jean-Michel. 2018. "Dependency Injection: the pattern without the framework." Medium, February 26. Accessed 2018-09-10.
- Fowler, Martin. 2004. "Inversion of Control Containers and the Dependency Injection pattern." January 23. Accessed 2018-09-10.
- Hiles, Paul. 2011. "How not to do dependency injection - the static or singleton container." DevTrends, July. Accessed 2018-09-10.
- Jenkov, Jakob. 2010. "Dependency Injection." YouTube, June 20. Accessed 2018-09-10.
- Jenkov, Jakob. 2014a. "Dependency Injection Benefits." Tutorials, May 26. Accessed 2018-09-10.
- Jenkov, Jakob. 2014b. "When to use Dependency Injection." Tutorials, May 25. Accessed 2018-09-10.
- Jenkov, Jakob. 2014c. "Is Dependency Injection Replacing the Factory Patterns?" Tutorials, May 25. Accessed 2018-09-10.
- Johansson, Mattias Petter. 2017. "Dependency Injection basics." Fun Fun Function, Medium, January 16. Accessed 2018-09-10.
- Johnson, Rod. 2006. "Spring Framework: The Origins of a Project and a Name." Spring Blog, Pivotal Software, November 09. Accessed 2018-09-12.
- Johnson, Ralph E. and Brian Foote. 1998. "Designing Reusable Classes." Journal of Object-Oriented Programming, vol. 1, no. 2, pp. 22-35, June/July. Accessed 2018-09-12.
- Loritsch, Berin. 2001. "Developing With Apache Avalon." Apache Avalon Project, Rev. 1.4, December 28. Accessed 2018-09-12.
- Luis, Andy. 2017. "A Brief History of Dependency Injection." MVP Java, May 17. Accessed 2018-09-10.
- Marston, Tony. 2011. "Dependency Injection is EVIL." Tony Marston's Blog, June 03. Updated 2016-10-01. Accessed 2018-09-10.
- Martin, Robert. 1994. "OO Design Metrics." Posted at comp.lang.c++, Google Groups, August 23. Accessed 2022-01-26.
- Martin, Robert. 1995. "The Principles of OOD." Posted at comp.lang.c++, Google Groups, June 06. Accessed 2022-01-26.
- Member 10409352. 2013. "Dependency Injection (DI)." Code Project, September 23. Accessed 2018-09-10.
- Microsoft Docs. 2010. "Dependency Injection." October 09. Updated 2018-04-24. Accessed 2018-09-10.
- Millar, Ethan. 2016. "How Dependency Injection (DI) Works In Spring Java Application Development". DZone, May 30. Accessed 2018-09-10.
- Nene, Dhananjay. 2005. "A beginners guide to Dependency Injection." The Server Side, July 01. Accessed 2018-09-10.
- Noback, Matthias. 2013. "Dependency injection smells." Blog, January 20. Accessed 2018-09-10.
- Pal, Tapas. 2017. "Dot NET Dependency Injection Frameworks." CodeGuru, November 27. Accessed 2018-09-10.
- Pankaj. 2018. "Java Dependency Injection – DI Design Pattern Example Tutorial." JournalDev, April 02. Accessed 2018-09-10.
- PicoContainer. 2017. "Inversion of Control History." PicoContainer, September 10. Accessed 2018-09-10.
- Singh, Rahul Rajat. 2013. "An Absolute Beginner's Tutorial on Dependency Inversion Principle, Inversion of Control and Dependency Injection." Code Project, July 08. Accessed 2018-09-10.
- Sinhal, Ankit. 2017. "Fundamentals of Dependency Injection and popular libraries in Android." AndroidPub, August 25. Accessed 2018-09-12.
- Smith, Steve, Scott Addie, and Luke Latham. 2018. "Dependency injection in ASP.NET Core." Microsoft Docs, February 07. Updated 2018-08-25. Accessed 2018-09-10.
- Wang, Chang. 2017. "You don't need to know Dependency Injection." Hacker Noon, November 20. Accessed 2018-09-10.
- Wikipedia. 2018. "Dependency injection." Wikipedia, August 29. Accessed 2018-09-10.
- mode13H. 2014. "Inversion of Control." Technical Deficit, January 21. Accessed 2018-09-12.
Further Reading
- Singh, Rahul Rajat. 2013. "An Absolute Beginner's Tutorial on Dependency Inversion Principle, Inversion of Control and Dependency Injection." Code Project, July 08. Accessed 2018-09-10.
- Johansson, Mattias Petter. 2017. "Dependency Injection basics." Fun Fun Function, Medium, January 16. Accessed 2018-09-10.
- Fayard, Jean-Michel. 2018. "Dependency Injection: the pattern without the framework." Medium, February 26. Accessed 2018-09-10.
- Luis, Andy. 2017. "A Brief History of Dependency Injection." MVP Java, May 17. Accessed 2018-09-10.
Article Stats
Cite As
See Also
- Inversion of Control
- Service Locator
- Software Design Patterns
- Factory Pattern
- Code Refactoring
- SOLID Design Principles
Article Warnings
- In References, replace these sub-standard sources: journaldev.com
- Readability score of this article is below 50 (49.6). Use shorter sentences. Use simpler words.