TypeScript - aop, ioc设计模式

既然使用了Typescript,自然会想到oop的那些设计模式。 传统的GOF设计模式,这里就不提及了,相关资料太多。 这里想讲述一下如何实现AOP、IOC两种设计模式。
一)AOP(面向切面编程)
aop在.net、java等平台上都有成熟的实现。举例说明其现实需求:假设有一个业务方法doSomeThing, 以及一个Logger日志类,现在需要记录下doSomeThing执行前后的log,该怎么做?
junior dev可能会这么做:

//以下示例使用ts
function doSomeThing(param:string):void {
   let logger = new Logger();
   logger.info('start');
   
    /* 执行 doSomeThing的原有逻辑 */
  
   logger.info('end');
}

class Logger {
  function info(param: any) { ... }
}

显然,这是最junior的做法,作为一个资深开发,会怎么做优化呢? AOP就是一个解决方案。 以下是代码示例:
package.json
"@babel/core": "^7.13.15",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-decorators": "^7.13.15",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-plugin-transform-typescript-metadata": "^0.3.2",
"reflect-metadata": "^0.1.13"

logger.ts


import "reflect-metadata";

export function LogDuration<T extends (...args: any[]) => any>(func: T): (...funcArgs: Parameters<T>) => ReturnType<T> {
    const funcName = func.name;
  
    // Return a new function that tracks how long the original took
    return (...args: Parameters<T>): ReturnType<T> => {
      console.time(funcName);
      const results = func(...args);
      console.timeEnd(funcName);
      return results;
    };
}

export function LogParam(value: string) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log(`方法名:${propertyKey}, 参数: ${value}`);
    };
}

业务代码文件:AOP.ts

import { LogDuration, LogParam } from '../utils/log/Logger';

function doSomeThing(param:string):void {
    /* 执行 doSomeThing的原有逻辑 */
}

class Foo {
  @LogParam('something')
  function doSomeThing() {}
}

export default LogDuration(doSomeThing)

上述代码中的LogParam通过TS的装饰器实现了aop,LogDuration则通过高阶函数的方式实现了aop
TS的装饰器,需要在配置文件tsconfig.json
"experimentalDecorators": true,
"emitDecoratorMetadata": true

二)IOC 控制反转
这里使用Inversify JS框架,package.json,加入:"inversify": "^5.0.5", 如下所示

{
  "name": "sample01",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@babel/core": "^7.13.15",
    "@babel/plugin-proposal-class-properties": "^7.13.0",
    "babel-plugin-transform-typescript-metadata": "^0.3.2",
    "inversify": "^5.0.5",
    "next": "10.1.3",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "reflect-metadata": "^0.1.13"
  },
  "devDependencies": {
    "@babel/plugin-proposal-decorators": "^7.13.15",
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "@zeit/next-typescript": "^1.1.1",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-decorators-legacy": "^1.3.5",
    "typescript": "^4.2.4"
  }
}

具体用法参考:https://inversify.io/https://github.com/inversify/InversifyJS

三) 最后再分析一下IOC和抽象工厂的区别
以下纯属个人理解:
1)使用IOC框架,我们可以灵活地将inject标签加在class的属性、方法,或构造函数的参数上。 灵活性远比抽象工厂好得多。
2)调用抽象工厂的时候,一般有两种方式,如下

function Foo(factory: MyFactory)
{
  let svc = factory.GetService();
}

//或如下
function Foo()
{
  let factory = new MyFactory();
  let svc = factory.GetService();
}

显然两种方式都对业务代码有侵入,都不如IOC来的好。

posted @ 2021-04-17 14:07  老胡Andy  阅读(946)  评论(0编辑  收藏  举报