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

2019-01-30  本文已影响0人  ChenReal

前言

        这一篇,我们谈谈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

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

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

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

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

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

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

上一篇 下一篇

猜你喜欢

热点阅读