1

Many people come from frameworks that implement Dependency Injection and IoC containers for everything (in my case Angular 2+), so, this group of people will try to use dependency injection and IoC containers in everything even outside those frameworks.

But, most of the implementation using DI is not using it to implement the Dependency inversion principle from SOLID (which is kind of impossible to implement without DI), they are just using it as a way of making the class easily testable doing something like this:

class Dependency {}

class SomeClass {
  constructor(private dependency: Dependency) {}
}

But why not this:

class Dependency {
  static global = new Dependency();
}

class SomeClass {
  protected dependency = Dependency.global;
}

We can easily mock/stub this dependecy only by extending it:


class TestSomeClass extends SomeClass {
  protected dependency = mock<Dependency>();
}

Is there any problem we could face by doing this instead of dependency injection?

8
  • Does this answer your question? Why should I prefer composition over inheritance?
    – gnat
    Dec 1, 2023 at 14:02
  • 2
    What's wrong with SomeClass objectUnderTest = new SomeClass(mock<Dependency>())? Dec 1, 2023 at 15:02
  • "most of the implementation using DI is not using it to implement the Dependency inversion principle from SOLID" - sure, a lot of times this is an afterthought; for dependency inversion to work, you have to be careful how you design the abstraction in the middle. But also, testing is one use case for that dependency inversion (your class not depending on the concrete implementation of the dependency is the reason why you can substitute a mock in the first place). Another thing this allows you to do is to control dependency direction between layers. Dec 1, 2023 at 15:07
  • @FilipMilovanović The problem is that your application will need: a IoC container or will need to have a class/file just to inject dependency or will need to pass dependency each time it needs to consume a class. It's kind of add additional work that can be avoided if you are not depending on abstractions. Dec 1, 2023 at 15:09
  • 2
    An IoC container is optional (google "Pure DI"), though in practice you'll often use one. The problem with a protected prop is that it's not dynamically configurable. Sometimes you want that, sometimes you don't. The problem with setting it from a global is, well, that it's a global (it's basically a variant of the Service Locator antipattern). It's like having a function using a global internally. Dec 1, 2023 at 15:17

1 Answer 1

4

It will be easier (and avoids the unneccessary usage of inheritance) by using an optional constructor parameter with a default value:

class SomeClass {
   constructor(private dependency = new Dependency()) {}
}

This is known as Bastard Injection - and as you see from that former link, even Mark Seemann (author of several well-known books about DI) thinks this is ok as long as "Dependency" is not a foreign dependency. It does not cause more or less dependencies than your proposed solution, requires less code and is simpler. For testing, you can stay with

  SomeClass objectUnderTest = new SomeClass(mock<Dependency>())

just as suggest by Filip Milovanović in a comment. And no, you don't need an IoC container for this.

Disclaimer: I am not a Typescript guy, I had to lookup the docs if Typescript supports this, it seems it does. If I made a syntactical error, feel free to fix it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.