如题,你是否常常听人提起依赖注入?反正笔者对这个概念已经听过屡次,也去搜索了解了许多知识,但每次看完都感受有点绕,因而作了一些实战来便于理解。前端
更多文章,欢迎 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 和 订阅 个人博客。