• 前端理解控制反转ioc


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

    这段时间对控制反转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<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进行详细测试。

       

  • 相关阅读:
    oracle中to_date详细用法示例(oracle日期格式转换)
    两个日期类型的值相减转化成秒
    oracle trunc 函数处理日期格式
    Oracle按时间查询条件
    Oracle获取两时间的相差天数
    杂 在tabControl设置上的小点
    C#基础 类(class)
    杂 关于tabControl
    未解,关于RichTextBox导致的前端问题
    添加Resources语言切换注意
  • 原文地址:https://www.cnblogs.com/legu/p/11249956.html
Copyright © 2020-2023  润新知