typescript - การพึ่งพาแบบวงกลมกับมัณฑนากร
ฉันได้รับการพึ่งพาแบบวงกลมกับมัณฑนากรของฉันเนื่องจากชั้นเรียนของฉันThingAมีความสัมพันธ์ThingBและในทางกลับกัน ฉันได้อ่านคำถามหลายข้อเกี่ยวกับปัญหานี้:
การแก้ไขที่สวยงามสำหรับปัญหาการพึ่งพาแบบวงกลมใน Javascript / typescript
TypeScript Decorators และ Circular Dependencies
แต่ฉันไม่พบวิธีแก้ปัญหาที่ถูกต้องสำหรับกรณีของฉัน ฉันพยายามตามที่หลายคนแนะนำให้เปลี่ยนจาก@hasOne(ThingA)เป็น@hasOne(() => ThingA)บังคับให้โหลดขี้เกียจและทำลายการพึ่งพา แต่วิธีนี้ใช้ไม่ได้ผลเพราะฉันไม่สามารถรับชื่อตัวสร้าง ฉันต้องการชื่อของตัวสร้าง (ตัวอย่าง: 'ThingA') เพื่อเพิ่มในข้อมูลเมตาของตัวสร้าง ThingB
ทำตามรหัสเดิมของฉัน (โดยไม่ต้องแก้ไข lazyload)
สิ่ง
@hasAtLeast(0, ThingB)
export class ThingA extends RTContent {
@IsEmail()
email: string;
}
สิ่งที่ B
@hasOne(ThingA)
export class ThingB extends RTContent {
@IsString()
description: string;
}
นักตกแต่ง:
export type LinkConstraint<C extends RTContent> = {
content: string; // name of the constructor (ex. 'ThingA')
maxOccurrences: number;
minOccurrences: number;
constructor: { new(...args: any[]): C };
}
function constraintFactory<C extends RTContent>(minOccurrences: number, maxOccurrences: number, associated: { new(...args: any[]): C }) {
return (constructor: Function) => {
const constraints = Reflect.getMetadata('linkConstraints', constructor) || [];
const constraint: LinkConstraint<C> = {
content: associated?.name,
minOccurrences,
maxOccurrences,
constructor: associated
};
constraints.push(constraint);
Reflect.defineMetadata('linkConstraints', constraints, constructor)
}
}
export function hasOne<C extends RTContent>(associated: { new(...args: any[]): C }) {
return constraintFactory(1, 1, associated)
}
export function hasAtLeast<C extends RTContent>(minOccurrences: number, associated: { new(...args: any[]): C }) {
return constraintFactory(minOccurrences, Infinity, associated)
}
คำตอบ
ฉันเห็นว่ามัณฑนากรของคุณไม่ได้แก้ไขตัวสร้างจริงๆมันเป็นเพียงการเรียกใช้โค้ดผลข้างเคียงบางอย่างเพื่อเพิ่มรายการข้อมูลเมตา ดังนั้น@decoratorไวยากรณ์จึงไม่จำเป็น
คำแนะนำของฉันคือคุณไม่ตกแต่งThingAหรือThingBส่งออกตามที่เป็นอยู่ คุณเลื่อนการตกแต่งในโมดูลอื่นซึ่งควรจะเป็นผู้ปกครองร่วมกันของทั้งสองและThingA ThingBวิธีนี้แก้ไขการพึ่งพาแบบวงกลม
ตัวอย่างเช่น'./things/index.ts'คุณทำ:
import { ThingA } from './things/ThingA';
import { ThingB } from './things/ThingB';
hasOne(ThingA)(ThingB);
hasAtLeast(0, ThingB)(ThingA);
export { ThingA, ThingB }
ตอนนี้ส่วนอื่น ๆ ของรหัสของคุณสามารถนำเข้าจากแทนโดยตรงจาก'./things/index.ts' './things/ThingA(or B).ts'สิ่งนี้จะช่วยให้มั่นใจได้ว่าการตกแต่งจะดำเนินการก่อนที่จะสร้างอินสแตนซ์ของคลาส
หากคุณต้องใช้มัณฑนากรการโหลดขี้เกียจเป็นทางออกที่ดีที่สุดของคุณ @hasOne(() => ThingA)ควรทำเคล็ดลับ แต่คุณต้องแก้ไขการใช้งานhasOneตามนั้นแฮ็คเล็กน้อย
function hasOne(target) {
if (typeof target === "function") {
setTimeout(() => {
_workOnTarget(target())
}, 0)
} else {
_workOnTarget(target)
}
}
กุญแจสำคัญคือการชะลอการเข้าถึงค่าตัวแปร
เพื่อให้แฮ็คนี้ใช้งานได้เรายังคงอาศัยข้อเท็จจริงที่ว่านักตกแต่งเหล่านี้เป็นผลข้างเคียงเท่านั้นอย่าแก้ไขตัวสร้าง ดังนั้นนี่ไม่ใช่วิธีแก้ปัญหาทั่วไปสำหรับปัญหาการพึ่งพาแบบวงกลม รูปแบบทั่วไปมากขึ้นคือการประเมินแบบขี้เกียจนอกหลักสูตร ซับซ้อนกว่านี้ถ้าคุณต้องการจริงๆกรุณาถามในความคิดเห็น
สำหรับกรณีของคุณ im ด้านบนควรใช้งานได้ แต่คุณต้องไม่สร้างอินสแตนซ์ThingAหรือ B ภายในระดับบนสุดของโมดูลใด ๆ เพราะจะเกิดขึ้นก่อนที่จะsetTimeoutโทรกลับจึงทำให้แฮ็คพังได้