在TS中如何在子进程中动态实例化一个类

背景:

假设当前有一个主进程A,一个子进程B和一个DemoTask

我们期望的是,在主进程A中通过IPC向子进程B发送一条指令,然后在子进程B中实例化DemoTask,并执行DemoTask中的的逻辑。

在TS中,并没有类似Java反射风格的调用机制,因此需要寻找一种适合TS的动态实例化的方法。

本文主要介绍在子进程B中如何动态实例化DemoTask,不涉及IPC通信的相关内容。

要求:

主进程A中不能import DemoTask,即不能在主进程中加载任何DemoTask的资源。

应该在子进程B中加载DemoTask的相关资源,并执行逻辑。

实现

思想

在子进程中做一个<类名 - 类> 的映射,然后根据"名称 ",找到具体的" ",最后直接"new"即可

举例1:极简实现

ts 复制代码
// DemoTask.ts
// 定义Task
export class DemoTask {
    // 略
}


//////////////////////////////////////////////////////////////////////////////


// TaskRepo.ts
import { DemoTask } from 'DemoTask'

// 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
// 在子进程中调用
export const TASK_REPO = {}

export function registerTask() {
    TASK_REPO["DemoTask"] = DemoTask;
}


//////////////////////////////////////////////////////////////////////////////


// Demo.ts
import { TASK_REPO } from 'TaskRepo'

// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
    let taskInstance = (new TASK_REPO["DemoTask"])();
    
    // 可以调用Task中的具体方法,略...

    return taskInstance
}

关键点:

  1. 在子进程B起来的时候,需要先执行 registerTask(),然后才能执行newTask() 进行实例化
  2. 需要手动维护 TASK_REPO 这个对象:当需要定义新的Task时,需要手动在此处补充Task属性

举例2:结合注解装饰器,实现自动注册

TS 复制代码
// DemoDecorator.ts 
/**
 * 关键:注册具体的Task实现类,用于在子进程中实例化具体的Task
 * 在子进程中调用
 */
export const TASK_REPO = {}

/**
 * 关键:定义注解
 * 该 注解(装饰器)可以向 TASK_REPO 中自动注册Task实现类
 * 在子进程中调用
 */
export function ProcessTask() {
  return (target): void => {
    TASK_REPO[target.name] = target;
  };
}


/////////////////////////////////////////////////////////////////////////////////////


// DemoTask.ts
// 定义Task:使用装饰器,实现自动注册Task
@ProcessTask()
export class DemoTask {
    // 略
}


/////////////////////////////////////////////////////////////////////////////////////


// Demo.ts
// 关键:实例化具体的Task
// 在子进程中调用
export function newTask() {
    let taskInstance = (new TASK_REPO["DemoTask"])();
    
    // 可以调用Task中的具体方法,略...

    return taskInstance
}

关于这个例子需要进一步讨论:

  • Q:在子进程中,类装饰器为什么没有触发执行?
    A:当加载DemoTask类时,会自动触发类装饰器;若没有触发类装饰器,说明DemoTask没有被加载到该进程。

  • Q:那该怎么办?
    A:在进程起来的时候执行一段"携带该Task的import代码 ",如,打印该Task的类名

    TS 复制代码
    import {DemoTask} from DemoTask.ts
    
    export const TASK_LOADER = [
      DemoTask
    ]
    
    // 在 子进程B 起来时,先调用执行这一段代码
    export function printDetail() {
      let array = PAF_TASK_LOADER  
      let taskNames = ""
      for (let task of array) {
        taskNames += (task.name) + " ## "
      }
      Logger.info(TAG, "succeed loading TASK. detail: length=" + array.length + " taskNames=" + taskNames)
    }
  • Q:"携带该Task的import代码"需要手写?
    A:可以开发"编译插件",在编译阶段自动生成上述代码

  • Q:那在"举例1"的基础上直接开发"编译插件",实现自动注册,岂不是更简单
    A:是的。但是引入装饰器,既可以在未来扩展DemoTask的功能,同时也不会使"编译插件"开发得过于复杂

相关推荐
不思念一个荒废的名字6 分钟前
【黑马JavaWeb+AI知识梳理】后端Web基础03 - MySQL概述
前端·数据库·mysql
橙子199110161 小时前
谈谈 Kotlin 中的构造方法,有哪些注意事项?
java·前端·kotlin
*neverGiveUp*2 小时前
本地分支git push 报错 fatal: The current branch XXXX has no upstream branch.
前端·git·gitea
AaronZZH2 小时前
为什么现代CSS应该选择OKLCH:从颜色科学到设计系统革新
前端·css
CaseyWei2 小时前
JS实现直接下载PDF文件
前端·javascript
pianmian13 小时前
3dczml时间动态图型场景
前端·javascript·数据库
编程大全3 小时前
45道工程模块化高频题整理(附答案背诵版)
前端·工程化
好奇的菜鸟3 小时前
如何升级 npm:从版本管理到最佳实践
前端·npm·node.js
伊成3 小时前
扫盲笔记之NPM
前端·笔记·npm
飞飞9873 小时前
spring mvc
java·服务器·前端