如題,你是否常常聽人提起依賴注入?反正筆者對這個概念已經聽過屢次,也去搜索瞭解了許多知識,但每次看完都感受有點繞,因而作了一些實戰來便於理解。前端
更多文章,歡迎 Star 和 訂閱 個人博客。mysql
要了解依賴注入,必須得先知道如下知識點。git
依賴倒置(Dependency inversion principle,縮寫爲 DIP)是面向對象六大基本原則之一。它是指一種特定的解耦形式,使得高層次的模塊不依賴於低層次的模塊的實現細節,依賴關係被顛倒(反轉),從而使得低層次模塊依賴於高層次模塊的需求抽象。github
該原則規定:web
上面這兩句話很抽象,須要細細品味才能發現其中奧祕,若是暫時理解不了也不要緊,下文會結合具體案例幫助你們理解。sql
控制反轉(Inversion of Control,縮寫爲 IOC)是面向對象編程中的一種設計原則,用來下降計算機代碼之間的耦合度。是實現依賴倒置原則的一種代碼設計思路。其中最多見的方式叫作依賴注入,還有一種方式叫依賴查找。數據庫
依賴注入(Dependency Injection,縮寫爲 DI)是實現控制反轉的一種方式。經常使用的依賴注入方法有 3 種:編程
在瞭解了相關理論以後,接下來咱們經過案例來理解一下依賴注入。後端
經常使用的後端架構能夠分爲 3 層:架構
接下來咱們經過非依賴注入,依賴注入,依賴注入容器這 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 和 訂閱 個人博客。