JavaScript Dependency Injection

后端 未结 12 1615
天涯浪人
天涯浪人 2020-12-22 16:51

I am new at JavaScript. I wonder how dependency injection is being implemented in JavaScript? I searched the internet but couldn\'t find anything.

12条回答
  •  既然无缘
    2020-12-22 17:24

    Let's learn it doing a super simple real world example :)

    The example class I am going to talk about here is a Printer which needs a driver to print something. I have demonstrated the advantages of dependency injection design pattern in 4 steps to arrive at the best solution in the end.

    Case 1: no dependency injection used:

    class Printer {
       constructor () {
          this.lcd = '';
       }
    
       /* umm! Not so flexible! */
       print (text) {
         this.lcd = 'printing...';
         console.log (`This printer prints ${text}!`);
       }
    }
    
    // Usage:
    var printer = new Printer ();
    printer.print ('hello');
    

    Usage is simple, it is easy to make a new printer this way but this printer is not flexible.

    Case 2: abstract the functionalities inside the print method into a new class called Driver:

    class Printer {
      constructor () {
        this.lcd = '';
        this.driver = new Driver ();
      }
    
      print (text) {
        this.lcd = 'printing...';
        this.driver.driverPrint (text);
      }
    }
    
    class Driver {
      driverPrint (text) {
        console.log (`I will print the ${text}`);
      }
    }
    
    // Usage:
    var printer = new Printer ();
    printer.print ('hello');
    

    So our Printer class is now more modular, clean and easy to understand but It is not flexible yet again. Any time you use new keyword you are actually hard-coding something. In this case you are constructing a driver inside your Printer which in real world is an example of a printer that comes with a built-in driver that can never change!

    Case 3: inject an already made driver into your printer

    A better version is to inject a driver at the time we construct a printer meaning you can make any type of printer, color or black & white, because this time the driver is being made in isolation and outside the Printer class and then given (INJECTED!) into the Printer

    class Printer {
      constructor (driver) {
        this.lcd = '';
        this.driver = driver;
      }
    
      print (text) {
        this.lcd = 'printing...';
        this.driver.driverPrint (text);
      }
    }
    
    class BWDriver {
      driverPrint (text) {
        console.log (`I will print the ${text} in Black and White.`);
      }
    }
    
    class ColorDriver {
      driverPrint (text) {
        console.log (`I will print the ${text} in color.`);
      }
    }
    
    // Usage:
    var bwDriver = new BWDriver ();
    var printer = new Printer (bwDriver);
    printer.print ('hello'); // I will print the hello in Black and White.
    

    Usage is now different, as a user, in order to have a printer you need to first construct (make) a driver (of your choice!) and then pass this driver to your printer. It may seem that end user now needs to know a bit more about the system, however this structure gives them more flexibility. Users can pass ANY driver as long as valid! for example let's say we have a BWDriver (black & white) type of driver; user can create a new driver of this type and use that to make a new printer that prints black and white.

    So far so good! But what you think we can do better and what you think has still some room to address here?! I am sure you can see it too!

    We are creating a new printer each time we need our printer to print with a different driver! That is because we are passing our driver of choice to the Printer class at the construction time; if user wants to use another driver they need to create a new Printer with that driver. For example, if now I want to do a color print I need to do:

    var cDriver = new ColorDriver ();
    var printer = new Printer (cDriver); // Yes! This line here is the problem!
    printer.print ('hello'); // I will print the hello in color.
    

    Case 4: provide a setter function to set the driver of your printer at ANY TIME!

    class Printer {
      constructor () {
        this.lcd = '';
      }
    
      setDriver (driver) {
        this.driver = driver;
      }
    
      print (text) {
        this.lcd = 'printing...';
        this.driver.driverPrint (text);
      }
    }
    
    class BWDriver {
      driverPrint (text) {
        console.log (`I will print the ${text} in Black and White.`);
      }
    }
    
    class ColorDriver {
      driverPrint (text) {
        console.log (`I will print the ${text} in color.`);
      }
    }
    
    // Usage:
    var bwDriver = new BWDriver ();
    var cDriver = new ColorDriver ();
    var printer = new Printer (); // I am happy to see this line only ONCE!
    
    printer.setDriver (bwDriver);
    printer.print ('hello'); // I will print the hello in Black and White.
    
    printer.setDriver (cDriver);
    printer.print ('hello'); // I will print the hello in color.
    

    Dependency Injection is not a really difficult concept to understand. The term may be a bit overloaded but once you have realised its purpose you will find yourself using it most of the time.

提交回复
热议问题