创建通用工厂 class
Create a generic Factory class
我有几个 Factory classes 做同样的事情:
export class XFactory {
private readonly validator: XValidator;
private readonly transformer: XTransformer;
private constructor( private readonly data: any, connection: Connection) {
this.validator = new XValidator(connection);
this.transformer = new XTransformer();
}
public static async build(data: any, connection: Connection) {
const factory = new XFactory (connection);
await factory.validate();
const dto = await factory.transform();
const x = new X(dto, connection);
return x;
}
private async validate(): Promise<void | never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<XDto> {
return await this.transformer.transform(this.data);
}
}
过程总是一样的:
- 验证数据,如果无效则抛出错误
- 将数据转换为构造函数使用的有效 DTO
- 实例化对象并return它
我在创建此 class 的抽象时遇到问题:
我试过这样的父工厂,但我有两个问题,标有星号:
export class Factory<T,U> {
private constructor(
private readonly data: any,
private readonly validator: Validator,
private readonly transformer: Transformer<U>
) {}
protected static
public static async build(data: any, validator: Validator, transformer: Transformer<U>*1, connection: Connection) {
const factory = new Factory (data, validator, transformer);
await factory.validate();
const dto: U*1 = await factory.transform();
const x: T*1 = new T*2(...[dto, connection]);
return x;
}
private async validate(): Promise<void|never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<XDto> {
return await this.transformer.transform(this.data);
}
}
*1 静态成员无法引用 class 类型参数:这是一个小问题,但我很想知道解决方法。
*2 找不到名称'T':有没有办法实例化这个class?我知道打字稿无法将其翻译成 js,我只是想写一些让我的意图明确的东西。
此外,构造函数方法是私有的,因为我不想将验证和转换与构造函数分开,但它们具有异步性质。
¿是否有另一种方法可以解决此问题,或者可以修改我的代码以便重构那些 XFactory classes 吗?
*1 Static members cannot reference class type parameters.
这就像问印刷机的作者是谁一样。出版社印了很多书,每本书都有不同的作者。该请求没有意义。
class 类型参数是针对每个实例的。不同的实例可能有不同的类型参数。除了约定之外,静态方法与 class 没有内在关系。它只是一个对相关功能进行分组的命名空间。只有按照惯例,这些函数才会执行与实例相关的任务。
我想你在这里想要的是静态 build
方法是通用的,因此它可以创建具有正确类型的 Factory
实例。然后你可以简单地使用那个实例。
// Make up some types to support what follows.
type Validator<Data> = { validate(data: Data): Promise<boolean> }
type Transformer<Data, Dto> = { transform(data: Data): Dto }
type Connection = { isConnected: boolean }
export class Factory<Data, Dto> {
private constructor(
private readonly data: Data,
private readonly validator: Validator<Data>,
private readonly transformer: Transformer<Data, Dto>
) {}
public static async build<Data, Dto, DtoInstance>(
data: Data,
validator: Validator<Data>,
transformer: Transformer<Data, Dto>,
dtoConstructor: new (dto: Dto, connection: Connection) => DtoInstance,
connection: Connection
): Promise<DtoInstance> {
const factory = new Factory(data, validator, transformer);
await factory.validate();
const dto = await factory.transform();
return new dtoConstructor(dto, connection);
}
private async validate(): Promise<void|never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<Dto> {
return await this.transformer.transform(this.data);
}
}
我已将此处的 T
和 U
分别重命名为 Data
和 Dto
。 Data
是输入格式,Dto
是转换的输出。
现在请注意 build
函数也是通用的。它接受自己的泛型类型参数(我使用了相同的名称,但它们完全没有联系)。这允许在调用 build()
时指定 Factory
构造函数的参数,然后设置实例的类型。
最后,build()
还接受一个构造函数,它将采用转换的输出和 return 某物的实例。我们从构造函数中捕获的实例类型作为泛型来设置 return 类型。
现在进行测试:
// mock a connection
const connection = { isConnected: true }
class TestDto {
public finalNum: number
constructor(data: { finalNum: number }, connection: Connection) {
this.finalNum = data.finalNum
}
}
async function test() {
const testDto = await Factory.build(
{ srcNum: 123 },
{ validate: async (data) => data.srcNum > 0 },
{ transform: (data) => ({ finalNum: data.srcNum }) },
TestDto,
connection
)
console.log(testDto) // TestDto: { finalNum: 123 }
console.log(testDto.finalNum) // 123
}
test()
综上所述...您为什么要在这里使用 class?如果它是一个纯函数,这将 far 简单。实现是3条短线。
async function build<Data, Dto, DtoInstance>({
data,
validate,
transform,
dtoConstructor,
connection
}: {
data: Data,
validate: (data: Data) => Promise<void>,
transform: (data: Data) => Dto,
dtoConstructor: new (dto: Dto, connection: Connection) => DtoInstance,
connection: Connection,
}): Promise<DtoInstance> {
await validate(data);
const dto = transform(data);
return new dtoConstructor(dto, connection);
}
你会这样使用:
// Make a reusable function bound to a specific Dto.
// Or maybe add as a static method of `TestDto`, `TestDto.build()`?
async function buildTestDto(data: { srcNum: number }) {
return await build({
data,
validate: async (data) => {
if (data.srcNum < 0) throw new Error("value must be positive")
},
transform: (data) => ({ finalNum: data.srcNum }),
dtoConstructor: TestDto,
connection,
})
}
async function go() {
const testDto = await buildTestDto({ srcNum: 123 })
console.log(testDto) // TestDto: { finalNum: 123 }
console.log(testDto.finalNum) // 123
const invalid = await buildTestDto({ srcNum: -456 }) // throws
console.log(invalid)
}
go()
我有几个 Factory classes 做同样的事情:
export class XFactory {
private readonly validator: XValidator;
private readonly transformer: XTransformer;
private constructor( private readonly data: any, connection: Connection) {
this.validator = new XValidator(connection);
this.transformer = new XTransformer();
}
public static async build(data: any, connection: Connection) {
const factory = new XFactory (connection);
await factory.validate();
const dto = await factory.transform();
const x = new X(dto, connection);
return x;
}
private async validate(): Promise<void | never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<XDto> {
return await this.transformer.transform(this.data);
}
}
过程总是一样的:
- 验证数据,如果无效则抛出错误
- 将数据转换为构造函数使用的有效 DTO
- 实例化对象并return它
我在创建此 class 的抽象时遇到问题:
我试过这样的父工厂,但我有两个问题,标有星号:
export class Factory<T,U> {
private constructor(
private readonly data: any,
private readonly validator: Validator,
private readonly transformer: Transformer<U>
) {}
protected static
public static async build(data: any, validator: Validator, transformer: Transformer<U>*1, connection: Connection) {
const factory = new Factory (data, validator, transformer);
await factory.validate();
const dto: U*1 = await factory.transform();
const x: T*1 = new T*2(...[dto, connection]);
return x;
}
private async validate(): Promise<void|never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<XDto> {
return await this.transformer.transform(this.data);
}
}
*1 静态成员无法引用 class 类型参数:这是一个小问题,但我很想知道解决方法。 *2 找不到名称'T':有没有办法实例化这个class?我知道打字稿无法将其翻译成 js,我只是想写一些让我的意图明确的东西。
此外,构造函数方法是私有的,因为我不想将验证和转换与构造函数分开,但它们具有异步性质。
¿是否有另一种方法可以解决此问题,或者可以修改我的代码以便重构那些 XFactory classes 吗?
*1 Static members cannot reference class type parameters.
这就像问印刷机的作者是谁一样。出版社印了很多书,每本书都有不同的作者。该请求没有意义。
class 类型参数是针对每个实例的。不同的实例可能有不同的类型参数。除了约定之外,静态方法与 class 没有内在关系。它只是一个对相关功能进行分组的命名空间。只有按照惯例,这些函数才会执行与实例相关的任务。
我想你在这里想要的是静态 build
方法是通用的,因此它可以创建具有正确类型的 Factory
实例。然后你可以简单地使用那个实例。
// Make up some types to support what follows.
type Validator<Data> = { validate(data: Data): Promise<boolean> }
type Transformer<Data, Dto> = { transform(data: Data): Dto }
type Connection = { isConnected: boolean }
export class Factory<Data, Dto> {
private constructor(
private readonly data: Data,
private readonly validator: Validator<Data>,
private readonly transformer: Transformer<Data, Dto>
) {}
public static async build<Data, Dto, DtoInstance>(
data: Data,
validator: Validator<Data>,
transformer: Transformer<Data, Dto>,
dtoConstructor: new (dto: Dto, connection: Connection) => DtoInstance,
connection: Connection
): Promise<DtoInstance> {
const factory = new Factory(data, validator, transformer);
await factory.validate();
const dto = await factory.transform();
return new dtoConstructor(dto, connection);
}
private async validate(): Promise<void|never> {
await this.validator.validate(this.data);
}
private async transform(): Promise<Dto> {
return await this.transformer.transform(this.data);
}
}
我已将此处的 T
和 U
分别重命名为 Data
和 Dto
。 Data
是输入格式,Dto
是转换的输出。
现在请注意 build
函数也是通用的。它接受自己的泛型类型参数(我使用了相同的名称,但它们完全没有联系)。这允许在调用 build()
时指定 Factory
构造函数的参数,然后设置实例的类型。
最后,build()
还接受一个构造函数,它将采用转换的输出和 return 某物的实例。我们从构造函数中捕获的实例类型作为泛型来设置 return 类型。
现在进行测试:
// mock a connection
const connection = { isConnected: true }
class TestDto {
public finalNum: number
constructor(data: { finalNum: number }, connection: Connection) {
this.finalNum = data.finalNum
}
}
async function test() {
const testDto = await Factory.build(
{ srcNum: 123 },
{ validate: async (data) => data.srcNum > 0 },
{ transform: (data) => ({ finalNum: data.srcNum }) },
TestDto,
connection
)
console.log(testDto) // TestDto: { finalNum: 123 }
console.log(testDto.finalNum) // 123
}
test()
综上所述...您为什么要在这里使用 class?如果它是一个纯函数,这将 far 简单。实现是3条短线。
async function build<Data, Dto, DtoInstance>({
data,
validate,
transform,
dtoConstructor,
connection
}: {
data: Data,
validate: (data: Data) => Promise<void>,
transform: (data: Data) => Dto,
dtoConstructor: new (dto: Dto, connection: Connection) => DtoInstance,
connection: Connection,
}): Promise<DtoInstance> {
await validate(data);
const dto = transform(data);
return new dtoConstructor(dto, connection);
}
你会这样使用:
// Make a reusable function bound to a specific Dto.
// Or maybe add as a static method of `TestDto`, `TestDto.build()`?
async function buildTestDto(data: { srcNum: number }) {
return await build({
data,
validate: async (data) => {
if (data.srcNum < 0) throw new Error("value must be positive")
},
transform: (data) => ({ finalNum: data.srcNum }),
dtoConstructor: TestDto,
connection,
})
}
async function go() {
const testDto = await buildTestDto({ srcNum: 123 })
console.log(testDto) // TestDto: { finalNum: 123 }
console.log(testDto.finalNum) // 123
const invalid = await buildTestDto({ srcNum: -456 }) // throws
console.log(invalid)
}
go()