共计 3236 个字符,预计需要花费 9 分钟才能阅读完成。
本篇是 SOLID 准则的最初一篇,倡议先浏览前四局部:
JavaScript 中的 SOLID 准则(一):“S”代表什么
JavaScript 中的 SOLID 准则(二):“O”代表什么
JavaScript 中的 SOLID 准则(三):“L”代表什么
JavaScript 中的 SOLID 准则(四):“I”代表什么
这是 SOLID 的第五篇文章(原文一共五篇),作者是 serhiirubets,感激继续关注。
依赖倒置准则
D – 依赖倒置准则 这个准则是指:高级模块不应该依赖低级模块;两者都应该依赖于形象,形象应该不依赖于细节,细节应该取决于形象。
举个例子,假如咱们想解决电影数据,咱们创立了一个简略的 Movie 类:
class Movie {constructor(title,description) {}}
咱们还须要保留视频信息到 localStorage,为了遵循 繁多职责准则,咱们独自创立一个类:
class MovieStorage {setItem(data) {}
getItemById(id) {}
getAll() {}
}
所有都很好,而且咱们的逻辑会在其余中央应用。
const movieStorage = new MovieStorage()
const ironMan = new MovieStorage('Iron man', 'Movie about Iron man')
const spiderMan = new MovieStorage('Spider man', 'Movie about Spider man')
movieStorage.setItem(ironMan)
movieStorage.setItem(spiderMan)
// here could be different other logic
movieStorage.getItemById(1);
如果咱们想把数据批改为存储到本地文件系统,没问题,再创立一个类:
class MovieFileStorage {save(data){}
editFile(data){}
readMovieById(id){}
readAllMovies(){}
}
当初咱们须要把之前应用 localStorage 的中央,替换成 fileStorage
看起来没有什么问题,咱们只是删除并替换了 4 行代码,但就像咱们之前探讨的,如果你在很多文件中屡次应用了 local Storage,很难找到所有应用的中央并正确的批改它们。而且如果你曾经为此写了测试,所有测试也须要进行批改。
批改后的代码能够失常工作,然而随着工夫的推移,对本地文件系统占用越来越大,咱们打算切换到数据库进行存储,MongoDB 或 SQL,咱们应该怎么做?遵循“繁多职责准则”,咱们创立了一个 DB 存储类:
class MovieDBStorage{insert(data){}
update(data){}
selectAll(){}
selectById(id){}}
当初咱们遇到了同样的问题,咱们须要查找所有的文件,把文件系统的逻辑批改为数据库操作,须要查找到所有相干文件中的调用并批改办法名,签名,为此编写的测试也须要进行调整。
应用的中央越多,批改起来越艰难,这也是导致代码呈现 bug 的起因之一。
心愿你能够了解这个问题,咱们来看看怎么能力防止它,还记得依赖倒置准则吗:高级模块不应该依赖低级模块;两者都应该依赖于形象,形象不应该依赖细节,细节应该取决于形象。
咱们从形象开始重构吧:咱们创立一个 MoveStorage 类,这个类将会是咱们的“形象”。形象不应该依赖细节,咱们应该怎么实现它呢?很简略,咱们为 MoveStorage 类创立办法,这些办法用来代替 MoveDBStorage,MovieFileStorage。
class MovieStorage {save(data) {}
edit(data) {}
getById(id) {}
getAll() {}
}
这个类就像一个接口,咱们所有的代码都会应用这些办法,它们的名称不会被扭转,也就是说咱们的高级模块 (咱们应用“形象”的中央) 将不依赖于咱们的外部逻辑。
接下来,咱们为每个存储形式创立特定的类,而且每个类应用的办法名、参数都和咱们的“形象”类保持一致:
class MovieFileStorage {save(data) {}
edit(data) {}
getById(id) {}
getAll() {}
}
class MovieDBStorage {save(data) {}
edit(data) {}
getById(id) {}
getAll() {}
}
最初咱们来调整咱们的“形象”:
class MovieStorage {constructor (storage) {this.storage = storage;}
save(data) {this.storage.save(data)
}
edit(data) {this.storage.edit(data)
}
getById(id) {this.storage.getById(id)
}
getAll() {this.storage.getAll()
}
}
当初咱们的“形象”曾经不依赖细节了,MovieStorage 接管任何存储的实例,并且实例遵循咱们的接口:
const movieStorage = new MovieStorage(new MovieFileStorage())
movieStorage.save(ironMan)
movieStorage.save(spiderMan)
moveStorage.getById(1)
如果咱们想把文件存储批改为缓存存储、本地 / 会话存储、MongoDB、SQL 等,咱们只须要筹备对应的存储类(用于 mongo、redis、sql),它应该实现和咱们的“形象”同名的办法,并把新的类实例传递到结构器中:
const movieStorage = new MovieStorage(new MovieDBStorage())
movieStorage.save(ironMan)
movieStorage.save(spiderMan)
movieStorage.getById(1)
咱们只是扭转了传递的参数:MovieStorage 接管的实例从 new MovieFileStorage()
批改成了new MovieDBStorage()
。咱们不须要查找并批改所有的文件,也不须要批改已有的测试。咱们所有的文件都应用了雷同的形象,而且咱们的形象不依赖于逻辑,形象即逻辑。
这就是 JS 中“SOLID”的收尾,心愿你能够在工夫中至多应用到他们中的一个。你能够全副应用,也能够只抉择一个,比方:繁多职责准则,查看你的代码是否都遵循了这个准则,如果没有,那就重构你的代码吧。
你也能够应用“依赖倒置准则”,并查看你的代码是否合乎这个准则,侥幸的是,像“Angular”或“NestJS”这些框架遵循了这个准则,你能够在应用他们的我的项目中看到具体的实际。
咱们来做个回顾吧:
1、繁多职责准则(SRP):一个类应该有且只有一个职责,解决一项特定工作。
2、凋谢关闭准则(OCP):一个类应该对扩大凋谢,对批改敞开。一个类在利用的其余中央曾经开始应用,就不应该再批改它。
3、里氏替换准则(LSP):派生的子类应该是可替换基类的,也就是说任何基类能够呈现的中央,都能够被子类替换。值得注意的是,当通过继承实现多态行为时,如果派生类没有恪守 LSP,可能会让零碎引发异样。
4、接口隔离准则(ISP):基类不应该蕴含他们子类不应用的办法,也就是说一个接口应该领有尽可能少的行为。应该把那些大而全的接口拆分成一些小的、具体的接口,这样客户端就只需关怀他们要用到的接口。
5、依赖倒置准则(DIP):高级模块不应该依赖低级模块,相同,他们应该依赖抽象类或者接口。也就是不应该在高级模块中应用具体的低级模块,应该听从依赖于形象 (接口) 而不是一个实例(类)。
感激浏览!欢送关注微信公众号”混沌前端“。