「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
前言
上一篇讲到了 JavaScript 怎么认识面向对象以及单一职责原则的应用,这一篇来讲一下依赖倒置原则。
什么是依赖倒置原则呢,网上一查,最常见的一句话就是,两个类(或者接口)不相互依赖而是依赖一个抽象类(或者接口)。多看几遍都快要背下来了,可是这么拗口的话怎么去理解。举一个例子就是 A 和 B 说话,但是他们不直接说话,而是通过 C 进行说话(这是什么剧情的既视感)。
这个 C 就是中间的工具人,接下来进入一个小剧场。
小剧场
某一天,A 对 C 说,你碰到 B 帮我告诉 B “******”。C 说好的,我记下了,然后等 B 来找 C 的时候,C说,这是 A 给你留下的 “*******”,你拿走吧。
一个很简单的小剧场,其实这就是依赖倒置原则的实现过程,就是 A 不直接和 B 碰面,而且通过留纸条的方式,C 就是管理纸条的人。
如果不这么抽象,稍微拉回现实一点,就是 A 不直接和 B 进行数据交流,而是将数据存在 C 那里,然后 B 是通过向 C 拿数据的过程。
用代码展示
用代码来写怎么实现呢?
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)
}
首先我们有一个 C 来存放数据,A 是获取了数据,然后存起来,B 要用这个数据是通过 C 来拿,而不是调用 A 或者通过 A 传参。这样哪怕接下来是换成 D 要使用这个数据也不用修改 A,来让 A 和 D 讲话。
只需要增加一个 D 方法
function D() {
const data = C.getData('temp1')
doSomeThingElse(data)
}
这种思想在很多地方都很常见,比如在 vue 项目中,我们通过接口请求数据,请求到了之后,存到 this.xxxx,然后渲染视图或者在下一个函数里面读取,然后做其他操作。
vuex 也是基于这种原则,两个组件不直接通信,而是可以通过存到 vuex 里面。这样无论组件怎么变化,如果想要这个数据就从 vuex 里面拿,如果要提供这个数据,就往 vuex 里面存。不需要关注和谁通信。
这只是依赖倒置这种思想的使用。依赖倒置的解释是依赖于抽象而不依赖于具体实现。
这样做的好处是什么呢?
依赖倒置原则的好处
依赖倒置原则可以让我们的代码更解耦。
假设有以下需求,第一获取到文章数据,第二把文章的某些字进行替换,第三将文章渲染到视图上。
async function getArticle() {
let article = await fetchArticle()
article = C.replaceStr(article)
B.render(article)
}
看起来还很容易,那现在更改需求,我们不要替换某些字了,而是引入一个翻译器,将文章翻译成其他语言,然后再渲染到视图上。
这时我们就会发现我们需要更改 getArticle,而且这个方法以及不满足单一职责原则,如果我们要引入其他操作文章的方法,就会让这个函数越来越大。如果我们最后不是渲染视图,我们可能是调接口上传处理后的数据,我们都需要改这个方法,如果加上不同类型用不同方法,我们就加上更多的条件判断,而获取文章,修改文章,处理修改后的文章应该是三个独立的棵替换的模块。
const store = {}
// 获取文章,不会改变
async function getArticle() {
const article = await fetchArticle()
handleArticle(article)
render()
}
// 依赖一个外部存储对象,也不会改变
function render() {
const article = store.article
B.render()
}
// 可选操作方法
function replaceStr(article) {
// do something
return article
}
// 可选操作方法
function translate(article) {
// do something
return article
}
// 操作
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
}
上面可以根据情况来更换 handleArticle,而获取数据,渲染视图的方法不需要再修改,让代码更容易维护。
我们可以随时更改我们的需求,如果我们不要渲染视图的话,我们只需要将 render 换掉,假如我们要将文章上传,我们只需要写一个方法,从 store.article 里面获取文章即可。
这样一来功能与功能之间就不再那么耦合了。
从某个角度来说它们都依赖于 "文章数据" 这个实体。这算不算也是一种依赖倒置呢。))))
以上就是从 JavaScript 的角度来看依赖倒置原则。设计是一门美学,好的代码不仅用起来优雅,项目复杂的时候也不会加班到头疼。