Why use Angular's dependency injection instead of new in the constructor?


#1

Angular's dependency injection provides a mechanism to define in the NgModule's providers, the parameters with which to construct a component.

I would like to understand the benefits of this mechanism compared to just using a new MyObject in the constructor of the component. I have read a chapter about this subject in the book "ng-book, the complete book on Angular". One can use DI with 3 types of dependencies: a value, a singleton instance of a class, a factory instantiating new classes.

  1. for the case of a singleton, one can use the fact that in ES 6 every "CommonJS module" is run only once when using import. I am mentioning an alternative to DI.

shared_dependency.ts:

const myUniqueInstance = new MyCLass()
export default myUniqueInstance

my_component.ts:

import myUniqueInstance from './shared_dependency'

However, there are small differences. For example, an import at the top of the file will be called when the Javascript file is loaded by the browser. With the Angular DI, the actual instantiation of myClass will be delayed until the first time it is needed. If it is never needed, it will not be done. It can be both good and bad. It speeds up the startup time and reduce memory use.

There is an structure difference. The COmmonJS singleton pattern looks like a global variable. The ANgular DI, will pass a variable as an input top the constructor. It is better programming style.

However, I almost never use the CommonJS singleton pattern. THe same can be achieved using new in the constructor. SO I still don't see the benefits of Angular DI.

  1. For the case of a factory, we can use:

constructor() {
    this.myUniqueInstance = new MyClass()
}

Or you can run new yourself somewhere else and pass it to the constructor.

For testing, one can mock override functions without DI by using babel transpiling. But it is a little tricky and Angular's DI may be a nicer way to provide mocks in the tests.

Maybe there is a structural benefit to put separated things outside of the component .
Maybe the memory is allocated differently when it is inside a a constructor and not like with the Angular DI.

So my question: what benefits are there with Angular's dependency injection compared to using new in the constructor? So far, I could not find one. The new in the constructor works fine. ONe can also run new ...() and pass the result to the constructor of the component.


#2

First off, I'd like to salute you on a well thought out and well written question!

The answer lies in the decoupling of the class that is used from the class that uses it, so that you can test them separately from each other using mocks, as you described, and also to make tests easier in case you need to create large object graphs for tests, which can be simplified using mocks.

Also, if you have a helper that does AJAX requests, you can mock that and just return some dummy data, which is way faster than actually doing an AJAX request.

This pattern is called Inversion of Control, you might want to read up on that, as it's very powerful indeed!

With regard to mocking, it's fine to use it, but make sure you don't mock what you don't own!


#3

The short answer is: Tight Coupling.

There's a good (TDD focussed) article here: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/ and I briefly explained tight coupling here: https://r.je/slutty-software-tight-and-loose-coupling.html

Yes, testing may be the first case where that tight coupling becomes an issue but you are building yourself into an inflexible corner. If you ever need to be able to substitute a dependency it becomes difficult.

Building flexibility into the application at the start is easy. Trying to introduce it after the project is 2 years old is difficult.


#4

I will also highlight the fact that using a DI container and programming to interfaces rather than implementations provides much more flexibility. Using interfaces makes it rather trivial to replace or override dependencies without hacking code which you might not own.


#5
  1. If you pass a parameter db to a constructor, you have also the possibility to mock the parameter by creating a MockDB mock version for your test. This is not inherent to Dependency Injection.
    I have a large tree of classes. In class A5 I do:
    db = new DatatbaseManager()
    this.a6 = new A6(db)

Note that Angular's DI slighly reduces the coupling since A5 may contain db for nothing.

  1. A large tree of classes may be better structured if you only instantiate objects in the small area where objects are needed. This is basic OOP. If you put everything in the NgModule, it can end up too verbose and mixing this that are only very specific to one class that could take care of that dependency on its own. Imagine the extreme case where all member variables in a class would be given as dependencies with DI. You could write the whole application in the NgModule.

  2. It is true that the new inside the constructor may be hard to test. But not necessarily. You can build some infrastructure like "babel rewire" (https://github.com/jhnns/rewire). I am sure there is something equivalent to do a rewire in Typescript. With rewire you can make a "mutant class", with the real class, thereby reducing the amount of code duplication since you use the real class.

  3. Here is one potential extra benefit of DI. In the book I mentioned, it says it manages the dependency's life cycle. This means it will delete it in good time instead of waiting for the garbage collector to find the unused reference to the instance. I am not sure about that since I did not check the Angular source. But potentially, the deletion may be smarter.

  4. For composition. One mix can dependencies with each other. There is a deps key in the NgModule. If you need Http in your dependency, you pass it in that way. There is a difference with using import Http inside the dependency's class. Like that all the "system libraries" can be passed from the NgModule. It maye be clearer to have this in one place, instead of propagating an instance of http in a large tree of classes.

  5. Dependency injection is not specific to Angular.
    https://stackoverflow.com/questions/14301389/why-does-one-use-dependency-injection
    pros: central place, modular testing, cons: one mega function knowing all dependencies.
    "This gives you a ton of advantages. Two important ones are the ability to control functionality from a central place (the Main() function) instead of spreading it throughout your program, and the ability to more easily test each class in isolation (because you can pass mocks or other faked objects into its constructor instead of a real value).

The drawback, of course, is that you now have one mega-function that knows about all the classes used by your program. That's what DI frameworks can help with. ..."


my understanding: more structured testing, clearer flow of dependencies centralized in the NgModule, better memory management, potentially a clean up of the top file imports that could be repeated in many files, reduced coupling for the places where the dependencies are located.

When it comes to OOP, it is not a new mechanism. It is equivalent to passing parameters to constructors in any OOP programming. The Angular mechanism will centralize this mechanism.


#6

Yes. If class A5 creates db 0 new DB and then passes it to the new A6, A5 contains db for nothing. It is unnecessary coupling. So Angular's DI, reduces the coupling by centralizing the DI mechanism.


#7

Also Angular doesn't have a single di container. Each component has its own DI container. Angular pages are a hierarchy of components each with a DI container. When a component requires a dependency it looks up the component hierarchy until it reaches container in the ancestory path that has the dependency. This how one is able to define services that override others and are only available to a single component.


#8

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.