“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

preface

The last article looked at how JavaScript understands object orientation and the application of the single responsibility principle. This article looks at dependency inversion.

If you look up the dependency inversion principle on the Web, the most common statement is that two classes (or interfaces) do not depend on each other but on an abstract class (or interface). Read more than a few times are about to recite down, but so mouthful words how to understand. For example, A and B talk, but they don’t talk directly, they talk through C (this is the deja vu of what story).

This C is the tool man in the middle, and then we go into a little theater.

Small theater

One day, A said to C, you met B to help me tell B “******”. C said ok, I wrote it down, and then wait for B to find C, C said, this is A left for you to “*******”, you take it.

A very simple little theater, in fact, this is the implementation of the dependency inversion principle, that is, A does not directly meet with B, and by leaving notes, C is the person who manages the notes.

If it’s A little less abstract, and A little more realistic, A doesn’t communicate with B directly, but stores the data with C, and THEN B gets the data from C.

Show it in code

How do I do that in code?

    const C = {
        cache: {}
        setData(data) {
            this.cache.temp1 = data
        },
        getData(key) {
            return this.cache[key]
        }
    }

    async function A() {
        const { data } = await fetchApi()
        C.setData(data)
    }

    function B() {
        cosnt data = C.getData('temp1')
        doSomeThing(data)
    }
Copy the code

First of all, we have A C to store the data, A gets the data and stores it, and B gets the data through C instead of calling A or passing parameters through A. So even if you want to use this data if you want to change it to D then you don’t have to change A to make A and D talk.

I just need to add a method D

    function D() {
        const data = C.getData('temp1')
        doSomeThingElse(data)
    }
Copy the code

This idea is common in many places, such as vue projects where we request data through the interface, get it, save it to this.xxxx, render the view or read it in the next function, and then do something else.

Vuex is also based on this principle, the two components do not communicate directly, but can be stored in vuex. So no matter how the component changes, if you want the data, you take it from Vuex, and if you want to provide the data, you store it in Vuex. You don’t care who you’re communicating with.

It just depends on the use of the idea of inversion. Dependency inversion is interpreted as relying on abstractions rather than concrete implementations.

What are the benefits of this?

Rely on the benefits of inversion

The dependency inversion principle makes our code more decoupled.

Suppose you have the following requirements: first, get the article data, second, replace some words of the article, and third, render the article to the view.

    async function getArticle() {
        let article = await fetchArticle()
        article = C.replaceStr(article)
        B.render(article)
    }
Copy the code

That seems easy enough, but now to change the requirements, instead of replacing some words, we introduce a translator that translates the article into another language and renders it to the view.

This is when we realize that we need to change getArticle, and that this method does not satisfy the single responsibility principle, and if we introduce other methods of manipulating articles, this function will get bigger and bigger. If we finally not render view, we may be a callback interface to upload data after processing, we all need to change the method, by adding different types with different methods, we will add more condition judgment, and access to the article, modify the article, dealing with the revised articles should be three separate replacement module.

    const store = {}
    
    // Get the article, no change
    async function getArticle() {
        const article = await fetchArticle()
        handleArticle(article)
        render()
    }
    // It depends on an external storage object
    function render() {
        const article = store.article
        B.render()
    }
    // Optional operation method
    function replaceStr(article) {
        // do something
        
        return article
    }
    // Optional operation method
    function translate(article) {
        // do something
        
        return article
    }
    / / operation
    handleArticle(article) {
        article = translate(article)
        
        store.article = article
    }
    
    // or
    
    handleArticle(article) {
        article = replaceStr(article)
        store.article = article
    }
    
    // or
    
    handleArticle(article) {
        article = replaceStr(article)
        article = translate(article)
        store.article = article
    }
Copy the code

HandleArticle can be changed as needed, and the method of fetching data and rendering the view does not need to be modified, making the code easier to maintain.

We can change our requirements at any time, if we don’t want to render the view, we just need to change the render, if we want to upload the article, we just need to write a method to get the article from the store.article.

In this way, functions are no longer coupled to each other.

In some sense they all depend on the entity called article data. This is also a dependency inversion.) )))

So that’s the dependency inversion principle from a JavaScript perspective. Design is an aesthetic, and good code is not only elegant to use, it also doesn’t cause headaches when you’re working on a complex project.