前端理解控制反转ioc
工作一直都是写前端,而且都是偏业务的。相关的框架代码层面的了解还是有很大的缺失。一直想多写些维护性,可读性强的代码。
这段时间对控制反转ioc,这样的设计有了一个初步的了解。
前端为弱语言,平时代码的时候主要是过程化的思路去解决问题。虽然也会定义一些class,但是和面向对象还是存在很大的差别的。
平时写的偏业务,也不需要抽象,一般也就直接写个实现类,再这个基础上面再进行扩展。主要是不存在类型检测之类的,可以随意一些,相对的错误也不大好发现。
控制反转ioc主要是用于解耦方面,下面看下解耦的最基本的原则
依赖倒置原则(Dependence Inversion Principle,DIP):
A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
B. 抽象不能依赖于具象,具象依赖于抽象。
简单点就是面向接口编程,具体实现具体类可以更换,只要是实现了某个接口就行。听起来像是鸭子类型
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
举个场景,比如当前页面结构
Page {
Header
Main {
SideBar
MainContent {
detail
comment
}
}
简易代码如下
class Detail {}
class Comment {
constructor() {
console.log('a');
}
}
class SideBar {}
class Header{}
class MainContent {
constructor() {
this.detail = new Detail();
this.comment = new Comment();
}
}
class Main {
constructor() {
this.sideBar = new SideBar();
this.mainContent = new MainContent();
}
}
class Page {
constructor() {
this.header = new Header();
this.main = new Main();
}
}
new Page();
我们的评论变了,换成了递归回复console.log('b'),最直接的就是修改Comment类的实现,但是我们的Comment存在a,b,c,d模式,并且同时都可能存在的,看用户的选择的该怎么处理呢??
我们可能就需要考虑在Page上面增加一个commentModel的参数,一直往下面传下去,把Comment类改成一个工厂,根据commentModel的参数返回不同的实现
class Detail {}
class SideBar {}
class Header{}
class CommentA {
constructor() {
console.log('a');
}
}
class CommentB {
constructor() {
console.log('a');
}
}
class CommentFactory {
constructor(commentModel) {
if (commentModel === 'a') {
return new CommentA();
} else if (commentModel === 'b') {
return new CommentB();
}
}
}
class MainContent {
constructor(commentModel) {
this.detail = new Detail();
this.comment = new CommentFactory(commentModel);
}
}
class Main {
constructor(commentModel) {
this.sideBar = new SideBar();
this.mainContent = new MainContent(commentModel);
}
}
class Page {
constructor(commentModel = 'a') {
this.commentModel = commentModel;
this.header = new Header();
this.main = new Main(commentModel);
}
}
new Page();
上面可以看到为了解决上面的需求,我们差不多所有的相关的类都改了一遍,可我们只是想让comment灵活配置,就这么麻烦么???所以我们就需要依赖倒置,让代码结构解耦
class Detail {}
class SideBar {}
class Header{}
class CommentA {
constructor() {
console.log('a');
}
}
class CommentB {
constructor() {
console.log('a');
}
}
class CommentFactory {
constructor(commentModel) {
if (commentModel === 'a') {
return new CommentA();
} else if (commentModel === 'b') {
return new CommentB();
}
}
}
class MainContent {
constructor(detail, comment) {
this.detail = detail;
this.comment = comment;
}
}
class Main {
constructor(sideBar, mainContent) {
this.sideBar = sideBar;
this.mainContent = mainContent;
}
}
class Page {
constructor(header, main) {
this.header = header;
this.main = main;
}
}
var mainContent = new MainContent(new Detail(), new CommentFactory('a'));
var main = new Main(new SideBar(), mainContent);
new Page(new Header, main);
依赖倒置后,我们只需要传入具体的实现类。对于类的定义,其实是依赖了接口,
现在项目中都是通过把page引用往内部传递,或者把page对象最为全局对象去使用,这样处理影响了代码的可维护性,公用性,耦合性
复杂项目中的依赖关系错综复杂,所以我们就需要引入控制反转控制器(Container)和依赖注入,让Container 管理 具体的对象,通过依赖注入,在代码层面绑定 变量和对象实例的关系,自动创建
下面就通过 typescript + reflect-metadata + inversify 来实现上面的代码
接口定义,大部分绑定都是接口,而不是具体的实现
//interfaces.ts 接口定义
export interface IComment {
}
export interface IDetail {
}
export interface ISideBar {
}
export interface IHeader {
}
export interface IComment {
}
export interface IMainContent {
detail: IDetail;
comment: IComment;
}
export interface IMain {
sideBar: ISideBar;
mainContent: IMainContent;
}
export interface IPage {
header: IHeader;
main: IMain;
}
//types.ts类型,其实可以理解为字符串
const TYPES = {
Detail: Symbol.for("Detail"),
SideBar: Symbol.for("SideBar"),
Header: Symbol.for("Header"),
Comment: Symbol.for("Comment"),
MainContent: Symbol.for("MainContent"),
Main: Symbol.for("Main"),
Page: Symbol.for("Page"),
}; export { TYPES };
//entities.ts 具体实现类
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { IComment, IDetail, ISideBar, IHeader, IMainContent, IMain, IPage} from "./interfaces";
import { TYPES } from "./types"; @injectable()
class Detail implements IDetail{}
@injectable()
class SideBar implements ISideBar{}
@injectable()
class Header implements IHeader{}
@injectable()
class CommentA implements IComment{
constructor() {
console.log('a');
}
}
@injectable()
class CommentB implements IComment {
constructor() {
console.log('b');
}
}
@injectable()
class MainContent implements IMainContent {
@inject(TYPES.Detail) detail: IDetail; //依赖注入
@inject(TYPES.Comment) comment: IComment;
}
@injectable()
class Main implements IMain {
@inject(TYPES.SideBar) sideBar: ISideBar;
@inject(TYPES.MainContent) mainContent: IMainContent;
}
@injectable()
class Page implements IPage {
@inject(TYPES.Header) header: IHeader;
@inject(TYPES.Main) main: IMain;
}
export { CommentA, CommentB, Detail, SideBar, Header, MainContent, Main, Page };
//inversify.config.ts 控制器定义,接口绑定对应的实现类
import { Container } from "inversify";
import { TYPES } from "./types";
import { IComment, IDetail, ISideBar, IHeader, IMainContent, IMain, IPage } from "./interfaces";
import { CommentA, CommentB, Detail, SideBar, Header, MainContent, Main, Page } from "./entities"; const containerA = new Container();
containerA.bind<IComment>(TYPES.Comment).to(CommentA);
containerA.bind<IDetail>(TYPES.Detail).to(Detail);
containerA.bind<ISideBar>(TYPES.SideBar).to(SideBar);
containerA.bind<IHeader>(TYPES.Header).to(Header);
containerA.bind<IMainContent>(TYPES.MainContent).to(MainContent);
containerA.bind<IMain>(TYPES.Main).to(Main);
containerA.bind<IPage>(TYPES.Page).to(Page); const containerB = new Container();
containerB.bind<IComment>(TYPES.Comment).to(CommentB);
containerB.bind<IDetail>(TYPES.Detail).to(Detail);
containerB.bind<ISideBar>(TYPES.SideBar).to(SideBar);
containerB.bind<IHeader>(TYPES.Header).to(Header);
containerB.bind<IMainContent>(TYPES.MainContent).to(MainContent);
containerB.bind<IMain>(TYPES.Main).to(Main);
containerB.bind<IPage>(TYPES.Page).to(Page); export { containerA, containerB };
//index.ts
import { containerA, containerB } from "./inversify.config";
import { TYPES } from "./types";
import { IPage } from "./interfaces"; containerA.get<IPage>(TYPES.Page);
containerB.get<IPage>(TYPES.Page);
当然我们也可以通过注入工厂方法的方式去修改下。
控制反转其实还有一个场景就是测试,A以来B,只是B的值简单处理,但是要构造一个具体的B对象很麻烦。我们可以通过ioc,把B对象替换成一个简单对象来对A进行详细测试。
前端理解控制反转ioc的更多相关文章
- 前端解读控制反转(IOC)
前言 随着前端承担的职责越来越重,前端应用向着复杂化.规模化的方向发展.大型项目模块化是一种趋势,不可避免模块之间要相互依赖,此外还有很多第三方包.这样的话如何去管理这些繁杂的文件,是一个不可避免的话 ...
- ASP.NET MVC进阶之路:深入理解依赖注入(DI)和控制反转(IOC)
0X1 什么是依赖注入 依赖注入(Dependency Injection),是这样一个过程:某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点.在程序运行过程中,客户 ...
- 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解
一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...
- 依赖注入(DI)和控制反转(IOC)的理解,写的太好了。
学习过spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。
轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战 ...
- 轻松理解 Java开发中的依赖注入(DI)和控制反转(IOC)
前言 关于这个话题, 网上有很多文章,这里, 我希望通过最简单的话语与大家分享. 依赖注入和控制反转两个概念让很多初学这迷惑, 觉得玄之又玄,高深莫测. 这里想先说明两点: 依赖注入和控制反转不是高级 ...
- 控制反转IoC简介
控制反转IoC简介 在实际的应用开发中,我们需要尽量避免和降低对象间的依赖关系,即降低耦合度.通常的业务对象之间都是互相依赖的,业务对象与业务对象.业务对象与持久层.业务对象与各种资源之间都存在这样或 ...
- 浅析“依赖注入(DI)/控制反转(IOC)”的实现思路
开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻.随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring.本文不 ...
- 控制反转IOC的依赖注入方式
引言: 项目中遇到关于IOC的一些内容,因为和正常的逻辑代码比较起来,IOC有点反常.因此本文记录IOC的一些基础知识,并附有相应的简单实例,而在实际项目中再复杂的应用也只是在基本应用的基础上扩展而来 ...
随机推荐
- 命令行启动python的IDLE
如果你电脑上使用了anaconda2,默认路径为python2,但是你又想使用anaconda2下的python3的idle 方法如下: 首先查看python的路径: (deeplearning3) ...
- NewLife.XCode 上手指南
想了解什么是XCode 在这里我不对XCode做过多介绍,XCode曾经是一个轻量级ORM组件,现在是一个重量级数据映射框架,支持实体对象数据到不同媒体的数据映射,提供面向对象的方式操作数据库,解决9 ...
- 终极解决办法rvct Cannot obtain license for Compiler (feature compiler) with license version >= 3.1
参考:https://blog.csdn.net/nic_r/article/details/7458038 ARM C/C++ Compiler, RVCT4. [Build ] armcc : e ...
- 静态文件cdn自解析生成相对路径
一.场景和目标: 用户上传一个包含 index.html 的静态资源压缩包,资源内所有文件都是相互依赖的,不需要用户对内部文件内容做任何特殊处理,仅通过服务端逻辑处理达到用户访问 http: ...
- hyperledger学习资料
http://www.cnblogs.com/aberic/p/7527831.htmlhttps://www.ibm.com/developerworks/cn/cloud/library/cl-t ...
- 为何有DAO与Service层?为何先搞Dao接口在搞DaoImpl实现?直接用不行吗?
转自 http://blog.sina.com.cn/s/blog_4b1452dd0102wvox.html 我们都知道有了Hibernate后,单独对数据的POJO封装以及XML文件要耗损掉一个类 ...
- PostgreSQL学习笔记——窗口函数
在学习窗口函数之前,我们新建一个Product表并往其中插入一些数据: drop table if exists Product; create table Product ( product_id ...
- sqlserver如何创建链接服务器
遇到下列问题: 线上服务器A,中转服务器B,本地服务器C 数据在A上面,想在B上面操作类似 select * from [A].[database].table这样的SQL,不用去链接服务器,直接把处 ...
- Egret入门学习日记 --- 第十四篇(书中 5.4~5.6节 内容)
第十四篇(书中 5.4~5.6节 内容) 书中内容: 总结 5.4节 内容重点: 1.如何编写自定义组件? 跟着做: 重点1:如何编写自定义组件? 文中提到了重要的两点. 好,我们来试试看. 第一步, ...
- Net上传文件
Net上传文件 最近工作内容涉及到一点前端的内容,把学习到的内容记录下来,在今后的开发过程中,不要犯错.本篇只针对一些刚入职的小白及前端开发人员,大牛请绕道!~ 刚开始我们先不讲上传文件的防范问题,先 ...