What is DI?

So dependency injection makes programmers' lives easier, but what does itreally do?

Consider the following code:

  1. class Hamburger {
  2. private bun: Bun;
  3. private patty: Patty;
  4. private toppings: Toppings;
  5. constructor() {
  6. this.bun = new Bun('withSesameSeeds');
  7. this.patty = new Patty('beef');
  8. this.toppings = new Toppings(['lettuce', 'pickle', 'tomato']);
  9. }
  10. }

The above code is a contrived class that represents a hamburger. The classassumes a Hamburger consists of a Bun, Patty and Toppings. The classis also responsible for making the Bun, Patty and Toppings. This is abad thing. What if a vegetarian burger were needed? One naive approach mightbe:

  1. class VeggieHamburger {
  2. private bun: Bun;
  3. private patty: Patty;
  4. private toppings: Toppings;
  5. constructor() {
  6. this.bun = new Bun('withSesameSeeds');
  7. this.patty = new Patty('tofu');
  8. this.toppings = new Toppings(['lettuce', 'pickle', 'tomato']);
  9. }
  10. }

There, problem solved right? But what if we need a gluten free hamburger?What if we want different toppings… maybe something more generic like:

  1. class Hamburger {
  2. private bun: Bun;
  3. private patty: Patty;
  4. private toppings: Toppings;
  5. constructor(bunType: string, pattyType: string, toppings: string[]) {
  6. this.bun = new Bun(bunType);
  7. this.patty = new Patty(pattyType);
  8. this.toppings = new Toppings(toppings);
  9. }
  10. }

Okay this is a little different, and it's more flexible in some ways, but it isstill quite brittle. What would happen if the Patty constructor changed toallow for new features? The whole Hamburger class would have to be updated.In fact, any time any of these constructors used in Hamburger's constructorare changed, Hamburger would also have to be changed.

Also, what happens during testing? How can Bun, Patty and Toppings beeffectively mocked?

Taking those concerns into consideration, the class could be rewritten as:

  1. class Hamburger {
  2. private bun: Bun;
  3. private patty: Patty;
  4. private toppings: Toppings;
  5. constructor(bun: Bun, patty: Patty, toppings: Toppings) {
  6. this.bun = bun;
  7. this.patty = patty;
  8. this.toppings = toppings;
  9. }
  10. }

Now when Hamburger is instantiated it does not need to know anything about itsBun, Patty, or Toppings. The construction of these elements has beenmoved out of the class. This pattern is so common that TypeScript allows it tobe written in shorthand like so:

  1. class Hamburger {
  2. constructor(private bun: Bun, private patty: Patty,
  3. private toppings: Toppings) {}
  4. }

The Hamburger class is now simpler and easier to test. This model of havingthe dependencies provided to Hamburger is basic dependency injection.

However there is still a problem. How can the instantiation of Bun,Patty and Toppings best be managed?

This is where dependency injection as a framework can benefit programmers, andit is what Angular provides with its dependency injection system.

原文: https://angular-2-training-book.rangle.io/handout/di/what_is_di.html