我怎样才能在 typescript 上整齐地处理100多个案件?

ee7vknir  于 2023-10-22  发布在  TypeScript
关注(0)|答案(1)|浏览(121)

通过在.env中写下批处理的类型,目标是能够用一个程序处理所有批处理。
希望用一个程序处理批次的目的是因为我不想为每个批次配置单独的CD。
即使一个程序使用相同的图像,我也想根据目的执行不同的操作,所以我使用. env。这是因为当程序执行一次时,只执行一种类型的批处理。
当batchTypes的数量很小时,我马上写了这个函数。

function oldOne(){
  const batchType = process.env.BATCH_TYPE

  if(batchType === EnumBatchType.AD_KEYWORD_INPUT){
    // do something about ad keyword input
  }else if(batchType === EnumBatchType.AD_KEYWORD_OUTPUT){
    //do something about ad keyword output
  }
  ...
  process.exit(0)
}

当我需要处理的消息数量超过10条时,我创建一个类,继承一个可以处理这些消息的接口。
很满足。

export interface Batchable{
    doBatch():Promise<void>; // every batch class has this function
}
------ InputBatch, OutputBatch, RefineBatch, and so on...
export abstract class InputBatch<T> implements Batchable {
  abstract getDataFromKurve(): Promise<T[]>

  abstract getBodyFromData(datas: T[]): Promise<NexusTagDto>

  abstract updateNexusTag(body: NexusTagDto): Promise<NexusResponseDto>

  async doBatch(): Promise<void> {
    const datas = await this.getDataFromKurve()
    const body = await this.getBodyFromData(datas)
    const res = await this.updateNexusTag(body)
    unifiedLog('batch-finish', JSON.stringify(res), true)
  }
}
------
export class Batch1 extends InputBatch<Something> {...}
export class Batch2 extends InputBatch<Something> {...}
export class Batch3 extends OutputBatch<Something> {...}
export class Batch4 extends RefineBatch<Something> {...}
export class Batch5 extends InputBatch<Something> {...}
export class Batch6 extends OutputBatch<Something> {...}
export class Batch7 extends InputBatch<Something> {...}
export class Batch8 extends InputBatch<Something> {...}

当然,每个批次都有一个名称,但出于安全原因,我将它们写为Batch 1,Batch 2,...
除开关和各批次代码外,所有代码如下。

export async function batchStart() {
  const batchType = process.env.BATCH_TYPE
  unifiedLog('batch_start', process.env.TARGET_SCHEMA)
  const client = getBatchClient(batchType as EnumBatchType)

  if (client) {
    await client.doBatch()
    process.exit(0)
  }
}

当我有超过50条信息要处理时,我意识到我所做的只是把一堆垃圾推到了视线之外。
问题是batchTypes的数量目前超过了60,因此,只有switch-case的getBatchClient函数超过了200行。
我们公司有规定,交换机必须按字母顺序排列。
现在我必须做大量的滚动来找到我应该把消息放在哪里。

function getBatchClient(batchType: EnumBatchType): Batchable {
  let client: Batchable
  switch (batchType as EnumBatchType) {
    // input
    case EnumBatchType.BATCH_TYPE1:
      client = new Batch1()
      break
    case EnumBatchType.BATCH_TYPE2:
      client = new Batch2()
      break
    case EnumBatchType.BATCH_TYPE3:
      client = new Batch3()
      break
    case EnumBatchType.BATCH_TYPE4:
      client = new Batch4()
      break
    case EnumBatchType.BATCH_TYPE5:
      client = new Batch5()
      break
....

    default:
      unifiedError('invaild_type', batchType)
      new SlackClient('Init Batch').sendErrorMessage({
        message: `${batchType} is invalid type (from ${process.env.JOBTAG})`,
        target: EnumNotiTarget.BATCHCODE
      })
      return null
  }
  return client
}

消息类型的数量可能会增加,我不喜欢看到太长的switch语句。
我也考虑过使用带有key-value message-client的map,但这不是一个好的选择,因为它只使用一个消息处理程序,并且程序会终止。
当我只使用一个类时,创建所有其他类感觉像是一种浪费。

function useMapCase(batchType: EnumBatchType): Batchable {
  const map = new Map<EnumBatchType, Batchable>([
    [EnumBatchType.BATCH_TYPE1, new Batch1()],
    [EnumBatchType.BATCH_TYPE2, new Batch2()],
    [EnumBatchType.BATCH_TYPE3, new Batch3()],
    [EnumBatchType.BATCH_TYPE4, new Batch4()],
    [EnumBatchType.BATCH_TYPE5, new Batch5()],
    [EnumBatchType.BATCH_TYPE6, new Batch6()]
  ])
  return map.get(batchType)
}

如果批处理的数量超过200,我将看到map构造函数超过200行。
我甚至没有权力,所以它是不可能改变布局,以便它可以设置在几个步骤如下所示。
如果我使用另一篇文章中的方法,它会像这样:

function otherWays(batchType: SomeObject): Batchable {
  const { batchType, level1, level2 } = batchType
  try {
    const batchTypeMap = new Map<string, BatchSelector>([
      ['input', new InputBatchSelector()],
      ['output', new OutputBatchSelector()],
      ['refine', new RefineBatchSelector()]
    ])

    const level1Map = batchTypeMap.get(batchType)
    const level2Map = level1Map.get(level1)
    return level2Map.get(level2)
  } catch (e) {
    unifiedError('invaild_type', batchType)
    new SlackClient('Init Batch').sendErrorMessage({
      message: `${JSON.stringify(batchType)} is invalid type (from ${process.env.JOBTAG})`,
      target: EnumNotiTarget.BATCHCODE
    })
    return null
  }
}

与上面的方法不同,它被划分为多个部分,因此如果我在执行get和process时创建了一个map,则不需要创建整个类。
虽然它不会只创建一个对象,但这是可以接受的。
所以,如果我能使用这种方法,我会很高兴,读到这篇文章的你也会很高兴。但是你可能无法理解,我无法确定batchType的文本。
所以不可能像那样拆分batchType,也不能使用它。
如果batchType中存在任何规律性,则会基于此做出决策,但没有这样的事情。
有什么办法吗?
我应该被几百个案子包围吗?
或者我应该使用map,即使它浪费内存,由于变量内部的一个对象,将不会被使用?

jdgnovmf

jdgnovmf1#

我也考虑过使用带有key-value message-client的map,但这不是一个好的选择,因为它只使用一个消息处理程序,并且程序会终止。
有人可能会说,如果在任何给定的运行中只使用一个批处理类,那么定义所有的批处理类同样是不必要的。
现在我必须做大量的滚动来找到我应该把消息放在哪里。
这告诉我,switch语句的根本问题是,在处理这个大列表时,它是一个痛苦。在这种情况下,无论它是一个大的switch还是一个大的Map或任何其他基于列表的解决方案上的初始化器,都将是一个痛苦。
假设EnumBatchType中的值可以有意义地转换为字符串,您可以考虑动态导入批处理类。那么除了文件系统之外,您在任何地方都没有大量的这些列表,您对任何一个批处理中的代码所做的更改在源代码控制中被明确标识为仅与该批处理相关,而不是同一源文件中定义的其他批处理(因为每个批处理都在自己的文件中)。
以下是这种方法的草图:
而不是:

export class Batch1 extends InputBatch<Something> {/*...*/}
export class Batch2 extends InputBatch<Something> {/*...*/}
export class Batch3 extends OutputBatch<Something> {/*...*/}
export class Batch4 extends RefineBatch<Something> {/*...*/}
export class Batch5 extends InputBatch<Something> {/*...*/}
export class Batch6 extends OutputBatch<Something> {/*...*/}
export class Batch7 extends InputBatch<Something> {/*...*/}
export class Batch8 extends InputBatch<Something> {/*...*/}

您可以将Batch1放在一个名为EnumBatchType.BATCH_TYPE1的字符串等价物的文件中(例如,batches/BatchType1.ts):

export default class Batch1 extends InputBatch<Something> {/*...*/}

类似地,batches/BatchType2.ts

export default class Batch2 extends InputBatch<Something> {/*...*/}

等等(对我来说,这是默认导出的极少数可接受的用法之一。
然后,获得要使用的正确批处理类大致如下:

export async function batchStart() {
    const batchType = process.env.BATCH_TYPE;
    unifiedLog('batch_start', process.env.TARGET_SCHEMA);
    const client = await getBatchClient(batchType as EnumBatchType);
    //             ^−−− note
  
    if (client) {
        await client.doBatch();
        process.exit(0);
    }
}

// ...

async function getBatchClient(batchType: EnumBatchType): Promise<Batchable> {
    const BatchClass = (await import(`./batches/${batchType}.ts`)).default as new () => Batchable;
    const client = new BatchClass();
    return client;
}

或者,如果你想让null在没有匹配的batch类时像当前一样返回,你可以让getBatchClient在batch类不存在的情况下处理导入错误:

async function getBatchClient(batchType: EnumBatchType): Promise<Batchable> {
    let importError = true;
    try {
        const BatchClass = (await import(`./batches/${batchType}.ts`)).default as new () => Batchable;
        importError = false;
        const client = new BatchClass();
        return client;
    } catch (error) {
        if (importError) {
            return null; // Like your current code does
        }
        throw error;
    }
}

在这两种情况下,这确实有一个问题,即如果这些文件之一的默认导出不是Batchable的有效构造函数,则代码将以TypeScript无法检测到的方式不正确,但它将大量列表的需求卸载到文件系统。

相关问题