preface

As the front-end takes on more and more responsibilities, front-end applications tend to be complex and large-scale. Modularity is a trend for large projects, with modules inevitably dependent on each other and many third-party packages. So how to manage these multifarious documents is an inevitable topic. At this point, a proven mode of thinking has been in favor: inversion of control (IOC).

The IOC is defined

Inversion of Control, or IoC, is a design principle used in object-oriented programming to reduce coupling between computer code. One of the most common is called Dependency Injection, or DI, and another is called Dependency Lookup. With inversion of control, when an object is created, it is passed a reference to the object on which it depends by an external entity that regulates all objects in the system. In other words, dependencies are injected into objects.

The principle of

  1. High-level modules should not depend on low-level modules. Both should rely on abstraction
  2. Abstractions should not depend on concrete implementations
  3. Programming for interfaces, not for implementations

For the front end, the concept of an interface is not as clear-cut as in a strongly typed language. The concept is rather boring, the following combined with examples may better understand a bit.

purpose

According to the concept, the main purpose is to reduce coupling and improve extensibility. Before delving further, let’s take a look at code coupling

Code coupling

The so-called coupling can be shown as follows:The code is too straightforward to relate to each other: if obj2 fails, the whole system fails too.

So our goal is to reduce the coupling between the two,

It’s a little bit clearer with the picture,

If the two are not so directly related, then the probability of mutual influence is much less.

Also, this is a relatively small number of modules, which is obviously not the case in a regular project. Imagine a multi-module scenario:In addition to coupling, the dependency between different gears is also a headache. After iterating several versions, IT is found that what is this thing, every move has a bug…

So the IOC is here to solve these problems. The common ways are dependency injection and dependency lookup. Js is best known for its extensive use of dependency injection in Angular. The text is rather pale, so we can look at it by example.

The instance

In the CASE of the NBA, if there are some stars and we want to know which team they belong to, it might look like this:

// Team info
class RTeam {
    constructor(){
        this.name = 'the rocket'}}// Player info
class Player{
    constructor(){
        this.team = new RTeam()
    }
    info(){
        console.log(this.team.name)
    }
}
/ / player ym
let ym = new Player()
ym.info() // 'rocket'

Copy the code

It looks good that the player player relies on a team RTeam to load the team when called. Now the control is in player.

Suppose at this time, the player has been traded, the team information has been changed, and the team2 has been switched. At this point, we need to modify the code in the player, because the players directly write the dependence on the RTeam, which is very poor scalability. This is not what we want, and we need to rethink dependency management. Does the relationship between players and teams have to be so direct and violent? With one player for one team, the possibility of change in the future is too high, since there is more than one team. If there is no direct connection between the two, there needs to be an intermediate module in the middle that handles the relationship between the two and the players don’t care where the team comes from, just give it to me. So control doesn’t rest directly with the player, which is exactly how the IOC is designed.

Improvement according to IOC

With reference to several principles of IOC, we will make improvements.

  1. High-level modules should not depend on low-level modules. So both of them should depend on abstraction and in this case the player is the higher level module, it depends directly on the team, the lower level module. So we decouple the two, and the player is no longer directly dependent on the team class

  2. The abstraction should not depend on the concrete implementation, the concrete implementation should depend on the Abstraction. To be specific, our Player module should not rely on the concrete team directly, but pass in the abstract TeamInfo instance through the constructor, thus decoupling the concrete implementation.

It’s easier to look directly at the code:

// Team information is not implementation-dependent
// Interface oriented is abstract oriented programming
class TeamInfo {
    constructor(name) {
        this.name = name
    }
}
class Player {
    // The argument here is an instance of teamInfo and does not depend directly on the specific instance
    // Abstraction oriented
    constructor(team) {
        this.team = team
    }
    info() {
        console.log(this.team.name)
    }
}
// Put dependencies here for management and control here
There is no longer a direct dependency between Player and TeamInfo
// The player that was directly in control of teamInfo is no longer directly dependent
// The dependency control, which falls here (managed exclusively by the third party module) is the inversion of control
var ym = new Player(new TeamInfo('the rocket'))
ym.info()
var kobe = new Player(new TeamInfo('the lakers'))
kobe.info()
Copy the code

As you can see here, TeamInfo and Player are no longer directly related, and the dependencies are put into getTeamInfo.

The so-called inversion of control is how to transfer control of a dependency from Player to somewhere else, as we did above. So add a team3, the change is not big, reuse on the line. The relationship between them is shown in the figure below:Do not directly contact each other, the dependency relationship unified in the middle module to manage, more clear.

implementation

In fact, the above is the simplest IOC implementation, based on IOC programming ideas, there are two main ways to achieve: dependency injection and dependency search. Dependency checking is less common; dependency injection is more common.

Dependency injection

Dependency injection is common in JS. Dependency injection, by its name, means that the dependencies between components are determined by the container at run time. Figuratively speaking, the container dynamically injects certain dependencies into the component.

The RequireJS/AMD module loader implementation is based on dependency injection, as is angular, which uses a lot of dependency injection.

conclusion

Inversion of control is where control is transferred from the consumer itself to the third party container, not to the called person. It is important not to be confused. Inversion of control is an idea and dependency injection is a design pattern. May sound more abstract, in fact, we usually see and use in the development is quite a lot, may not correspond to the original. As for Dependency injection, it is used more in the front-end field. I will translate a good article on Dependency injection- injection-in-javascript based on my own practice to further explore Dependency injection. At this point, my personal opinions are shared and I hope to learn and progress together. For more articles please visit my blog