如題,你是否常常聽人提起依賴注入?反正筆者對這個概念已經聽過屢次,也去搜索瞭解了許多知識,但每次看完都感受有點繞,因而作了一些實戰來便於理解。前端
更多文章,歡迎 Star 和 訂閱 個人博客。
要了解依賴注入,必須得先知道如下知識點。mysql
依賴倒置(Dependency inversion principle,縮寫爲 DIP)是面向對象六大基本原則之一。它是指一種特定的解耦形式,使得高層次的模塊不依賴於低層次的模塊的實現細節,依賴關係被顛倒(反轉),從而使得低層次模塊依賴於高層次模塊的需求抽象。git
該原則規定:github
上面這兩句話很抽象,須要細細品味才能發現其中奧祕,若是暫時理解不了也不要緊,下文會結合具體案例幫助你們理解。web
控制反轉(Inversion of Control,縮寫爲 IOC)是面向對象編程中的一種設計原則,用來下降計算機代碼之間的耦合度。是實現依賴倒置原則的一種代碼設計思路。其中最多見的方式叫作依賴注入,還有一種方式叫依賴查找。sql
依賴注入(Dependency Injection,縮寫爲 DI)是實現控制反轉的一種方式。經常使用的依賴注入方法有 3 種:數據庫
在瞭解了相關理論以後,接下來咱們經過案例來理解一下依賴注入。編程
經常使用的後端架構能夠分爲 3 層:後端
web 層架構
service 層
database 層
接下來咱們經過非依賴注入,依賴注入,依賴注入容器這 3 種思路,搭建一個簡單的後端 3 層架構。
非依賴注入的開發模式很符合常規邏輯,即:web 層依賴 service 層,service 層依賴 database 層。
案例以下:
// 案例中使用僞代碼,便於你們理解。 class Database { select(sql) { const mysql = require('mysql'); return new Promise(resolve => { // 鏈接數據庫,並執行 sql 語句進行查詢 mysql.createConnection().query(sql, (error, results, fields) => { const success = results.length > 0 ? true : false; resolve(success); }); }); } } class Service { async login(username, password) { const db = new Database(); const success = await db.select( `select * from user where username=${username} and password=${password}` ); return success ? '登陸成功' : '登陸失敗'; } } class Web { matchRouter(path) { switch (path) { case 'login': const service = new Service(); const { username, password } = path.query; return service.login(username, password); } } } // 使用 web 層 const web = new Web(); web.matchRouter('login');
上面的代碼是典型的高層次模塊依賴低層次模塊案例。web 層依賴 service 層,service 層依賴 database 層。
非依賴注入開發模式的優缺點:
若是把上面的案例改形成依賴注入的方式也很簡單,刪除內部依賴關係,將須要的依賴經過構造函數注入就好了。
// 案例中使用僞代碼,便於你們理解。 class Database { select(sql) { const mysql = require('mysql'); return new Promise(resolve => { // 鏈接數據庫,並執行 sql 語句進行查詢 mysql.createConnection().query(sql, (error, results, fields) => { const success = results.length > 0 ? true : false; resolve(success); }); }); } } class Service { constructor(db) { this.db = db; } async login(username, password) { // const db = new Database(); const success = await this.db.select( `select * from user where username=${username} and password=${password}` ); return success ? '登陸成功' : '登陸失敗'; } } class Web { constructor(service) { this.service = service; } matchRouter(path) { switch (path) { case 'login': // const service = new Service(); const { username, password } = path.query; return this.service.login(username, password); } } } // 使用 web 層以前,必須手動建立依賴,並注入 const database = new Database(); const service = new Service(database); const web = new Web(service); web.matchRouter('login');
上面的代碼能夠看出,web 層並不直接依賴 service 層,而是經過構造函數將 service 傳進來直接用,這就實現了依賴注入的效果。
依賴注入開發模式的優缺點:
若是使用上面的案例,每一次使用都須要手動傳入依賴,當依賴太多時,也會形成難以維護的問題。咱們能夠在一個地方統一進行依賴注入,即在一個依賴注入容器裏。
一個簡單的依賴注入容器以下:
// ioc.js export default function createIoC() { const iocMap = new Map(); return { bind(key, callback) { iocMap.set(key, { callback }); }, use(key) { const { callback } = iocMap.get(key); return callback(); } }; }
在統一的配置文件中配置依賴關係。
// ioc-config.js import createIoC from 'ioc.js'; const ioc = createIoC(); // 手動綁定依賴關係 ioc.bind('Database', () => { return new Database(); }); ioc.bind('Service', () => { const database = ioc.use('Database'); return new Service(database); }); ioc.bind('Web', () => { const service = ioc.use('Service'); return new Web(service); }); export default ioc;
使用容器注入依賴。
import ioc from 'ioc-config.js'; // 使用 web 層 const web = ioc.use('Web'); web.matchRouter('login');
上面代碼使用 IOC 容器來進行依賴注入,優缺點以下:
本文經過非依賴注入,依賴注入,IOC 容器這 3 種開發模式來分析了依賴注入的開發方式。加深了筆者對依賴注入的理解,但願經過這個案例能讓更多的同窗弄懂依賴注入。
更多文章,歡迎 Star 和 訂閱 個人博客。