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来的好。