我有幾個工廠類做同樣的事情:
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
- 實體化物件并回傳
我在創建此類的抽象時遇到問題:
我試過這樣的父工廠,但我有兩個問題,用星號標記:
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 靜態成員不能參考型別別引數:這是一個小問題,但我很想知道一個解決方法。*2 找不到名稱“T”:有沒有辦法實體化這個類?我知道打字稿無法將其翻譯成 js,我只是想寫一些東西來表達我的意圖。
建構式方法也是私有的,因為我不想將驗證和轉換與建構式分開,但它們具有異步性質。
?是否有另一種方法可以解決這個問題,或者可以修改我的代碼以便重構那些 XFactory 類?
uj5u.com熱心網友回復:
*1 靜態成員不能參考型別別引數。
這就像問印刷機它的作者是誰。出版社出版了許多書,所有書的作者都不一樣。請求沒有意義。
型別別引數是每個實體。不同的實體可能有不同的型別引數。除了約定之外,靜態方法與它們所在的類沒有內在關系。它只是一個用于對相關功能進行分組的命名空間。并且只有按照慣例,這些函式才會執行與實體相關的任務。
我認為您在這里想要的是靜態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函式如何也是通用的。它接受自己的泛型型別引數(我使用了相同的名稱,但它們完全斷開連接)。這允許Factory建構式的引數在build()被呼叫時被特別輸入,然后設定實體的型別。
最后,build()還接受一個建構式,它將接受轉換的輸出并回傳某個東西的實體。我們從建構式中捕獲作為泛型的實體型別以設定回傳型別。
現在來測驗一下:
// 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()
操場
說了這么多……你為什么在這里使用一個類?這將是迄今為止簡單,如果它是一個純粹的功能。實作是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()
操場
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/399051.html
