纯TypeScript开发Web前端(五)依赖注入

前言

        这一篇,我们谈谈TypeScript的设计模式,也是对我自己Demo的一个总结。如果说前面几篇所讲的基本都是新手入门,那么这里开始算是进阶的内容。

依赖注入(Dependency Injection)

        依赖注入,简称DI。近年非常火,相信如果有订阅一些技术文章推送的朋友,隔三差五会收到一些文章介绍DI的原理然后附带各种IoC框架的推介。不少朋友尽管早已耳熟能详,甚至对其原理和用途随口都能讲出来,可是,一旦动手实践起来总会觉得比较以难入手。最后,只能将探索行动抛在一边去,直接堆框架,开启无脑调用模式。
        其实,我想说,DI的实现并没有那么玄乎。也许,在你自己的项目你也曾经不经意地用过,但是没有意识到这就是DI而已。实现DI有很多种方法,我并不打算面面俱到,从概念到原理到编码这样讲。相信这些文章随便一搜就一大把。我主要分享一下我自己在TypeScript项目中,所实践出来的DI套路。

1、模块

        要实现依赖注入,首先,得有模块。模块可以作为注入的零件,也快是被注入的操控者。先看看我的模块定义:

    src/
    ├── app.ts
    ├── libs/
    │   └── router.ts
    ├── modules/
    │   ├── product/
    │   │   ├── list.ts
    │   │   └── detail.ts
    │   └── user/
    │       ├── login.ts
    │       └── info.ts
    └── utils/
        ├── http.ts
        ├── template.ts
        └── cache.ts

这个其实在之前也有分享过的。我的模块主要分两大类

  • 工具类(Utils)

例如:router(路由)、http、template(模板)、cache(缓存)

  • 业务类(Modules)

例如:user/login(用户登录)、user/info(用户中心)、product/list(产品列表)、product/detail(产品详情)

        很容易你就可以判断出来,在我的应用场景下,基本上都是业务类的模块依赖于工具类的模块,来实现相关的业务功能的。拿产品列表来举个栗子:

  • GetProductList - 请求服务端产品数据,需要用到http
  • RenderData - 将产品列表数据在页面渲染出来,需要用到template
  • ShowProductDetail - 打开产品详情页,需要先将产品数据放到cache里,然后product/detail从cache获取数据,无需再多走一次http请求;而模块页面的跳转,需要用到路由router的方法

那么按照,那么传统的做法。就要这么写:

import { Router } from "../../libs/router";
import { Http } from "../../utils/http";
import { DataCache } from "../../utils/cache";
import { Template } from "../../utils/template";

export class ProductList {
  GetProductList(){
    var http = new Http();
    http.Post(...);
  }
  RenderData(){
    var tpl = new Template();
    tpl.Parse(...);
  }
  ShowProductDetail(){
    var cache = new DataCache();
    cache.Set(...);
    var router = new Router();
    router.Go(...);
  }
}

        这样的代码看着,似乎还好吧?只能说没有对比就没有伤害:)
        想象一下,如果有几十个业务模块都需要类似这样引用,单单复制粘贴就要几十次。万一要改一下构建函数加个参数呢?都是纯体力活呐~
厌恶了这样的体力活之后,你会首先想到的是DI。

2、容器

        有了模块之后,最好有一个容器(Container)把所有工具模组装起来,然后作为一个对象参数注入到业务模块当中。个人认为这是最省时最省力的方法!当然,容器并不是必选项,你可以选择单个单个的注入。那么,同样面临着如果需要扩增注入模块要大面积的改代码的问题。

        在TS里面DI容器可以简单定义成一个接口,然后把里面的对象实都例化,然后就可以用于注入了。

interface DIContainer {
  http? : Http;
  router? : Router;
  cache? : DataCache;;
  tpl? : Template;
}

const container : DIContainer = { 
  http: new Http() ;
  router: new Router() ;
  cache: new DataCache() ;
  tpl: new Template() ;
}

        其实,也可以弄的稍微复杂一些,可以让容器中的模块先不初始化,而是建立一个工厂模型,需要用到的时候再去初始化。那么容器的弹性会强一些,至少不会一开始占用那么多资源。

interface DIContainer {
    http?: Http;
    router?: Router;
    cache?: DataCache;
    tpl?: Template;
    Build(key: string): void;
}

class Container implements DIContainer {
    http: Http;
    router: Router;
    cache: DataCache;
    tpl: Template;

    Build(key: string) {
        switch (key) {
            case "http":
                if (!this.http) this.http = new Http();
                break;
            case "router":
                if (!this.router) this.router = new Router();
                break;
            case "cache":
                if (!this.cache) this.cache = new DataCache();
                break;
            case "tpl":
                if (!this.tpl) this.tpl = new Template();
                break;
        }
    }
}
var container :Container();
container.Build("http");
container.Build("tpl");
container.Build("cache");
container.Build("router");
3、注入

        好了,说了半天,总算“万事俱备只欠注入”了!直接上代码:

export class ProductList {
  constructor(private di : DIContainer) {}
  GetProductList(){
    this.di.http.Post(...);
  }
  RenderData(){
    this.di.tpl.Parse(...);
  }
  ShowProductDetail(){
    this.di.cache.Set(...);
    this.di.router.Go(...);
  }
}
var proList = new Product(container); //把上面的容器作为构建参数注入

        就是这样,把容器作为模块构建参数注入。这样容器里面的模块就能随便用了~哈哈,看懂了之后,是不是应该拍手称快呢?

4、控制

        反转控制(Inversion of Control),这算是属于纯概念的东西。真要讲的话可以扯上一千多字,我就懒得再去绕了。
        反正,依赖注入已经搞定,然后注入的容器中的对象也都用起来了,耦合度大大降低了,代码越看越帅了,心情舒爽不少,胃口也好了。那么,就已经达到我的目的了。至于到底谁控制了谁,自己体会吧。跟我半毛钱关系都没有~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容