Original Source: https://dev.to/martinhaeusler/understanding-dependency-injection-by-writing-a-di-container-from-scratch-part-1-1hdf
Baby steps in Java for understanding Dependency Injection.
- Code is very poor in terms of OO principles. main.services.ServiceA and main.services.ServiceB should, at the very least, be objects.
- The code is tightly coupled and very hard to test in isolation.
- We have absolutely no chance of swapping neither main.services.ServiceA nor main.services.ServiceB with a different implementation. Imagine one of them is doing credit card billings; you don't want that to actually happen in your test suite.
- We can now replace the implementation of
main.services.ServiceB
which is used bymain.services.ServiceA
by providing another object, potentially even of another subclass. - We can test both main.services in isolation with a proper test mock for main.services.ServiceA
- It's hard to create mocks, as we require a class.
- It would be much nicer if we require interfaces instead. Also, this would further reduce the coupling.
- As your project gets bigger, it will become more and more complex to create the network of services inside your
main()
method. - your will encounter cycles in your service dependencies which cannot be resolved using constructors as shown in our example.
- Doing the wiring part manually is error prone. You might forget to call a setter and then it's
NullPonterException
galore. - You might accidentally use a service instance that is still under construction, so it would be beneficial to encapsulate the network construction somehow.
- Service get wired together automatically.
- We can no longer forget to call a setter (in fact, we don't need them anymore).
- Our application will fail on startup if the wiring fails, not during the business logic.
- We want to have the ability to access every service in the network, not just one.
- We now can easily pull out any service instances from the context by calling
getServiceInstance
- The main.services itself can access each other simply by declaring a field of the proper type
- The main.services don't even have to know about the
DIContext
object.
- We need a way to tell our algorithm which fields we want it to set and which ones to leave alone.
- Now the algorithm knows which fields it needs to assign
- How we can actually discover our service classes?
- Our classpath scanner will detect all classes and decide if they are services or not.