通过在 .env 中记下批次类型,目标是能够使用一个程序处理所有批次。
想要用一个程序处理批次的目的是因为我不想为每个批次配置单独的CD。
即使一个程序使用相同的图像,我也想根据目的执行不同的操作,所以我使用了.env。这是因为程序执行一次时只执行一种类型的批处理。
当batchType数量很少时,我立即编写了该函数。
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> {...}
当然,每个批次都有一个名称,但出于安全原因,我将它们写为 Batch1、Batch2、...
除去开关和各批代码,全部代码如下。
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 条消息需要处理时,我意识到我所做的只是将一堆垃圾推到看不见的地方。
问题是目前batchType数量超过了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 语句。
我也考虑过使用带有键值消息客户端的映射,但这不是一个好的选择,因为它只使用一个消息处理程序并且程序终止。
当我只使用一个类时,创建所有其他类感觉像是一种浪费。
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,我会看到地图构造函数超过 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 时创建一个映射并处理它,则不需要创建整个类。
虽然它不只创建一个对象,但这是可以接受的。
所以,如果我能使用这个方法,我会很高兴,读到这篇文章的你也会很高兴。但你可能无法理解,我无法确定batchType的文本。
所以不可能像这样拆分batchType并且不能使用。
如果batchType有规律的话,就会据此做出决定,但不存在这种情况。
有什么办法吗?
我应该只承保数百个案例吗?
或者我应该使用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
,您可以让
getBatchClient
处理导入错误(如果批处理类不存在):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 无法检测到的方式不正确,但它会卸载文件系统需要大量列表。