Typescript IOC控制反转 101 (1): 写个简单的IOC容器

611 阅读3分钟

最近把Spring重学一遍, 总算是理清IOC, DI, AOP的概念, 想整理一下, 写篇笔记, 又想锻鍊自己的ts类型体操 (^_^), 所以就用Typescript来篇IOC文章吧

为什么要用IOC容器

通常说IOC容器, 第一时间会想到Spring, 它可以说是IOC最成熟的实践, 但IOC不是java或Spring独有, 它是一种设计模式, 主要是用来解决软件工程里的耦合问题。那什么是耦合? 所谓耦合指的是类之间, 或模块之间的依赖, 如果两个或以上的类或模块紧密耦合, 意味着只要改其中之一, 就会影响另一个类,也意味着我们要改另一个的代码, 不然会出现错误。强耦合, 某方面来说, 也可以说是我们日常所说 "屎山代码"的代名词, 当我们改了一段代码, 要不就某个地方报错, 甚至可能整个项目无法运行。所以说, 解耦合, 是软件开发经常要处理的问题。

我们日常开发中, 如果要创建一个类, 在js/ts中, 我们会直接new一个类, 高级些的开发者可能会考虑到耦合问题, 会用接口思想来解决, 就像以下代码:

// User.ts
export interface User {
  getName: () => string,
  getAge: () => number
}

// UserImpl1.ts
import { User } from './interface_demo'

export class UserImpl implements User {

   private name:string;

   private age:number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }


  public getName():string {
    return "User1的名字是" + this.name;
  }

  public getAge():number {
    return "User1的年龄是" this.age;
  }
}

//demo.ts
import { UserImpl1 } from "./UserImpl1";
import { User } from './interface_demo';

const user: User = new UserImpl1("Tom", 45);

console.log(user.getAge()); // "User1的名字是Tom"
console.log(user.getName()); // "User1的年龄是45"

利用工厂模式解耦合

上述代码有问题吗? 功能上一定是没有问题, 但如果改需求, 不要 UserImpl1, 要改 UserImpl2, 意味着我要在代码上去改, 一个还好, 如果是有好几个, 就要去好几个地方去改。这意味着什么? 耦合, UserImpl的改动会影响到涉及到它的模块的运行。有什么比较好的解决方法? 加一个中间层, 通常软件工程问题, 没有什么加中间层解决不了。我们可以用工厂模式去解决。工厂模式指的是指类的创建统一交给一个类来处理:

// BeanFactory.ts
import { UserImpl } from './UserImpl';

type BeanFactoryType = Record<string, Object>;

const beanFactory: BeanFactoryType = {
  'userImpl': new UserImpl("Tom", 45)
}

type beanTypes = keyof BeanFactoryType;

export function getBean<T>(key: beanTypes): T {
  const result = beanFactory[key] as T;
  return result;

}

//demo.ts
import { UserImpl1 } from "./UserImpl1";
import { getBean } from "./BeanFactory";

const user: User = getBean<User>('userImpl');

console.log(user.getAge()); // "User1的名字是Tom"
console.log(user.getName()); // "User1的年龄是45"

配置文件解耦参数

这样子如果要改需求, 把 UserImpl1 改为 UserImpl2, 只需要去 BeanFactory文件改就可以了, 减少改动, 意味着鬆耦合。所以现在没有问题了? 看了一下 BeanFactory, 发现构造方法传参是硬编码进法, 意味着耦合, 所以要再加一层中间层, 我们利用json文件作为参数的配置文件。 (注意: Typescript下, 引入json文件, 要先在tsconfig.json中把 resolveJsonModule 设为 true):

// beanConfig.json
{
  "user": {
    "name": "Tom",
    "age": 45
  }
}
import { UserImpl } from './UserImpl';
import * as config from './beanConfig.json';

type BeanFactoryType = Record<string, Object>;

const beanFactory: BeanFactoryType = {
  'userImpl': new UserImpl(config.user.name, config.user.age)
}

type beanTypes = keyof BeanFactoryType;

export function getBean<T>(key: beanTypes): T {
  const result = beanFactory[key] as T;
  return result;
}

总算是完成了一个小型的IOC容器了, 简单来说就是利用工厂模式和配置文件, 做到类(或是函数)和类的构造方法传入的参数解耦合, 我们可以把所有的类创建交给 IOC容器处理, 一旦需求改变, 可以减少代码的改动。

源碼地址: github.com/DominguitoL…