具有不同参数的构造函数的工厂方法?

问题描述 投票:0回答:1

我正在开发一个软件,需要在满足某些条件时触发警报。

  1. 开发了一个抽象Alert类,使用
    send(message:string)
    作为发送警报的方法。
abstract class Alert {
    abstract send(message:string):Promise<void> {}
}
  1. 继承自Alert开发了DiscordAlert和TelegramAlert。
class DiscordAlert extends Alert {
    constructor(private discordHookUrl:string){}
    async send(message:string) {
        console.log(`Sending Discord alert to ${this.discordHookUrl}`);
    }
}

class TelegramAlert extends Alert {
    constructor(private token:string){}
    async send(message:string) {
        console.log(`Sending Telegram alert to ${this.token}`);
    }
}

我注意到 DiscordAlert 和 TelegramAlert 都有不同的构造函数参数。 因为discord 使用

discordHookUrl
而 telegram 使用
token

现在的问题是我想为警报创建一个工厂,但不知道如何传递配置。

class AlertFactory {
    static createAlert(type, config?????) {
        switch (type) {
            case 'discord':
                return new DiscordAlert(config???);
            case 'telegram':
                return new TelegramAlert(config???);
            default:
                throw new Error(`Alert type ${type} not supported.`);
        }
    }
}

我怎样才能做到这一点?

  1. 我应该传递所有警报的完整配置吗?
  2. 有没有办法以通用方式传递配置?
  3. 我应该传递像
    Record<string,string>
    这样的配置并在创建任何警报之前进行运行时类型检查吗?
  4. 我是否应该使用IDiscordAlertConfig这样的接口,然后完整的配置将符合警报接口?
typescript oop design-patterns polymorphism factory
1个回答
0
投票

通常最好的解决方法是让你的函数采用元组的并集。

例如:

function foo(...args: [type: 'a', value: string] | [type: 'b', value: number]) {
  //...
}
foo('a', 'a string') // fine
foo('b', 123) // fine

foo('a', 456) // error

这表示该函数接受两个参数。它们可以是

'a'
string
,也可以是
'b'
和数字。但它们不能
'a'
和一个数字。

但是我们需要派生一些不同的类型才能使您的代码正常工作。


为了更好地测试事物,让我们确保两个更改构造函数采用不同的类型。

class DiscordAlert extends Alert {
    constructor(private discordHookUrl: string) { super() }
    async send(message: string) {}
}

class TelegramAlert extends Alert {
    constructor(private token: { jwt: string }) { super() }
    async send(message: string) {}
}

现在我们需要一个类型来将

discord
这样的字符串映射到该名称的构造函数。可能看起来像这样:

type AlertMapping = {
  discord: typeof DiscordAlert,
  telegram: typeof TelegramAlert,
}

请注意,我们希望

typeof MyClassHere
获取类的 constructor 的类型,而不是该类的实例的类型。

从中我们可以将元组与映射类型进行并集:

type AlertArgs = {
    [K in keyof AlertMapping]: [
        type: K,
        config: ConstructorParameters<AlertMapping[K]>[0]
    ]
}[keyof AlertMapping]

这映射了

AlertMapping
中的所有键,并为每个键创建一个元组类型,其中第一个元素是键本身
K
,第二个是在
AlertMapping
中找到的构造函数的第一个参数的类型在那个键上。

现在您可以将该类型用于您的函数函数参数:

class AlertFactory {
    static createAlert(...[type, config]: AlertArgs): unknown {
        switch (type) {
            case 'discord':
                return new DiscordAlert(config);
            case 'telegram':
                return new TelegramAlert(config);
            default:
                throw new Error(`Alert type ${type} not supported.`);
        }
    }
}

现在我们可以调用此方法,第一个参数

type
将根据构造函数的需要与第二个参数
config
强链接。

// Fine
const discordAlert = AlertFactory.createAlert('discord', 'hookUrlHere')
const telegramAlert = AlertFactory.createAlert('telegram', { jwt: 'abc123' })


// Error as expected
const discordAlertBad = AlertFactory.createAlert('discord', { jwt: 'abc123' })

看游乐场

© www.soinside.com 2019 - 2024. All rights reserved.