博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
前端理解控制反转ioc
阅读量:5025 次
发布时间:2019-06-12

本文共 6995 字,大约阅读时间需要 23 分钟。

工作一直都是写前端,而且都是偏业务的。相关的框架代码层面的了解还是有很大的缺失。一直想多写些维护性,可读性强的代码。

这段时间对控制反转ioc,这样的设计有了一个初步的了解。

     前端为弱语言,平时代码的时候主要是过程化的思路去解决问题。虽然也会定义一些class,但是和面向对象还是存在很大的差别的。

     平时写的偏业务,也不需要抽象,一般也就直接写个实现类,再这个基础上面再进行扩展。主要是不存在类型检测之类的,可以随意一些,相对的错误也不大好发现。

     控制反转ioc主要是用于解耦方面,下面看下解耦的最基本的原则

  依赖倒置原则(Dependence Inversion Principle,DIP):

  A. 上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
  B. 抽象不能依赖于具象,具象依赖于抽象。

  简单点就是面向接口编程,具体实现具体类可以更换,只要是实现了某个接口就行。听起来像是鸭子类型

      “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

     举个场景,比如当前页面结构

Page {        Header        Main {        SideBar            MainContent {                detail                comment            }    }

      简易代码如下      

1 class Detail {} 2 class Comment { 3     constructor() { 4         console.log('a'); 5     } 6 } 7 class SideBar {} 8 class Header{} 9 class MainContent {10     constructor() {11         this.detail = new Detail();12         this.comment = new Comment();13     }14 }15 16 class Main {17     constructor() {18         this.sideBar = new SideBar();19         this.mainContent = new MainContent();20     }21 }22 23 class Page {24     constructor() {25         this.header = new Header();26         this.main = new Main();27     }28 }29 30 new Page();
View Code

 

     我们的评论变了,换成了递归回复console.log('b'),最直接的就是修改Comment类的实现,但是我们的Comment存在a,b,c,d模式,并且同时都可能存在的,看用户的选择的该怎么处理呢??

     我们可能就需要考虑在Page上面增加一个commentModel的参数,一直往下面传下去,把Comment类改成一个工厂,根据commentModel的参数返回不同的实现

1 class Detail {} 2 class SideBar {} 3 class Header{} 4 class CommentA { 5     constructor() { 6         console.log('a'); 7     } 8 } 9 class CommentB {10     constructor() {11         console.log('a');12     }13 }14 class CommentFactory {15     constructor(commentModel) {16         if (commentModel === 'a') {17             return new CommentA();18         } else if (commentModel === 'b') {19             return new CommentB();20         }21     }22 }23 class MainContent {24     constructor(commentModel) {25         this.detail = new Detail();26         this.comment = new CommentFactory(commentModel);27     }28 }29 class Main {30     constructor(commentModel) {31         this.sideBar = new SideBar();32         this.mainContent = new MainContent(commentModel);33     }34 }35 class Page {36     constructor(commentModel = 'a') {37         this.commentModel = commentModel;38         this.header = new Header();39         this.main = new Main(commentModel);40     }41 }42 new Page();
View Code

 

  上面可以看到为了解决上面的需求,我们差不多所有的相关的类都改了一遍,可我们只是想让comment灵活配置,就这么麻烦么???所以我们就需要依赖倒置,让代码结构解耦

1 class Detail {} 2 class SideBar {} 3 class Header{} 4 class CommentA { 5     constructor() { 6         console.log('a'); 7     } 8 } 9 class CommentB {10     constructor() {11         console.log('a');12     }13 }14 class CommentFactory {15     constructor(commentModel) {16         if (commentModel === 'a') {17             return new CommentA();18         } else if (commentModel === 'b') {19             return new CommentB();20         }21     }22 }23 class MainContent {24     constructor(detail, comment) {25         this.detail = detail;26         this.comment = comment;27     }28 }29 class Main {30     constructor(sideBar, mainContent) {31         this.sideBar = sideBar;32         this.mainContent = mainContent;33     }34 }35 class Page {36     constructor(header, main) {37         this.header = header;38         this.main = main;39     }40 }41 var mainContent = new MainContent(new Detail(), new CommentFactory('a'));42 var main = new Main(new SideBar(), mainContent);43 new Page(new Header, main);
View Code

 

    依赖倒置后,我们只需要传入具体的实现类。对于类的定义,其实是依赖了接口,

    现在项目中都是通过把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
(TYPES.Comment).to(CommentA);containerA.bind
(TYPES.Detail).to(Detail);containerA.bind
(TYPES.SideBar).to(SideBar);containerA.bind
(TYPES.Header).to(Header);containerA.bind
(TYPES.MainContent).to(MainContent);containerA.bind
(TYPES.Main).to(Main);containerA.bind
(TYPES.Page).to(Page);const containerB = new Container();containerB.bind
(TYPES.Comment).to(CommentB);containerB.bind
(TYPES.Detail).to(Detail);containerB.bind
(TYPES.SideBar).to(SideBar);containerB.bind
(TYPES.Header).to(Header);containerB.bind
(TYPES.MainContent).to(MainContent);containerB.bind
(TYPES.Main).to(Main);containerB.bind
(TYPES.Page).to(Page);export { containerA, containerB };
//index.tsimport { containerA, containerB } from "./inversify.config";import { TYPES } from "./types";import { IPage } from "./interfaces";containerA.get
(TYPES.Page);containerB.get
(TYPES.Page);

 

当然我们也可以通过注入工厂方法的方式去修改下。

 

控制反转其实还有一个场景就是测试,A以来B,只是B的值简单处理,但是要构造一个具体的B对象很麻烦。我们可以通过ioc,把B对象替换成一个简单对象来对A进行详细测试。

 

   

转载于:https://www.cnblogs.com/legu/p/11249956.html

你可能感兴趣的文章
Bzoj 3343: 教主的魔法
查看>>
括号序列(栈)
查看>>
一件趣事
查看>>
atom 调用g++编译cpp文件
查看>>
H3C HDLC协议特点
查看>>
iptables 网址转译 (Network address translation,NAT)
查看>>
ios __block typeof 编译错误解决
查看>>
android 插件形式运行未安装apk
查看>>
ios开发之 manage the concurrency with NSOperation
查看>>
Android权限 uses-permission
查看>>
NSEnumerator用法小结
查看>>
vim如何配置go语言环境
查看>>
机器学习好网站
查看>>
python 中的 sys , os 模块用法总结
查看>>
解题:国家集训队 Middle
查看>>
响应者链
查看>>
指针从函数内部带回返回值
查看>>
在使用webView播放flash或视频文件时无法关闭声音的问题
查看>>
redhat 7 源码安装 mysql5.5.49
查看>>
CCP浅谈
查看>>