تزریق وابستگی بدون دکوراتور در TypeScript

دکوراتورها تغییرات بزرگی در TypeScript 5 خواهند داشت، و در حالی که دکوراتورها جالب هستند، در مورد خلاص شدن از شر آنها چطور؟ و اگر بتوانیم DI را بسازیم چه می شود بسیار در طول راه بهتر است؟
نکته: پیاده سازی آماده برای استفاده از DI مورد بحث در این مقاله را می توانید در اینجا بیابید
همانطور که من به زبان های دیگر و همچنین با اکوسیستم های DI تثبیت شده کار کردم، می بینم که یک چارچوب DI خوب باید:
- شفاف باشید – دامنه شما باید نه هر چیزی که مربوط به DI باشد را وارد کنید و اصلاً نباید از DI اطلاع داشته باشد
- از رابطهای بدون تزئینات احمقانه با نام رابط پشتیبانی کنید
- به طور خودکار پیاده سازی را برای رابط هایی که فقط یک بار پیاده سازی می شوند ارائه دهید
- وقتی در یک بسته تولیدی کامپایل می شود، باید به خوبی کار کند
- Autowires – به این معنی است که به طور خودکار هر چیزی را که می تواند بدون پیکربندی یا با حداقل پیکربندی ایجاد می کند
تطبیق آن نقاط با استفاده از TypeScript بی اهمیت نیست. من فعلاً دکوراتورها و ویژگیهای بازتاب آزمایشی آنها را نادیده میگیرم و بر آنچه در تایپ اسکریپت 5 خواهیم داشت تمرکز خواهم کرد.
در این راه چند مانع وجود دارد:
- TypeScript هیچ پشتیبانی بازتابی ارائه نمی دهد
- به دلیل عدم بازتاب، دریافت انواع پارامترهای سازنده کلاس ها غیرممکن است
- Runtime همچنین تمام کلاس های شما را از قبل بارگذاری نمی کند، بنابراین باید آنها را وارد کنید جایی به طوری که اعلان کلاس اجرا می شود و کلاس تعریف می شود
- رابط ها در حین کامپایل پاک می شوند و در کدهای در حال اجرا معنی صفر دارند
- دریافت لیستی از رابط های پیاده سازی شده توسط یک کلاس مشخص غیرممکن است
خشن. اما به نظر می رسد که غلبه بر همه آن مشکلات و مطابقت با تمام ویژگی های کلیدی یک پیاده سازی خوب DI ممکن است.
کلید اینجاست انعکاس.
خوب من خودم انجامش میدم
حتی چگونه به این موضوع نزدیک شویم؟ ما باید اطلاعاتی در مورد هر کلاس در برنامه و اطلاعات زیادی از آن، از جمله رابط های پیاده سازی شده و پارامترهای سازنده، بدون دکوراتور به دست آوریم. درست…
اگر بتوانیم پایگاه کد خود را اسکن کنیم و هر فایلی را بخوانیم، اعلانهای کلاس را در آنها پیدا کنیم، آنها را تجزیه کنیم و دادههای لازم را از آن استخراج کنیم؟ حتما باید راهی باشه
API کامپایلر TypeScript را وارد کنید.
API کامپایلر را می توان برای به دست آوردن یک نمایش توکن شده از کد منبع استفاده کرد.
بنابراین میتوانیم برای رسیدن به آنچه میخواهیم کارهای زیر را انجام دهیم:
- برای فایلهای تایپاسکریپ، فهرست منبع را اسکن کنید
- برای هر فایل:
- آن را تجزیه کنید تا درخت نحو انتزاعی را به دست آورید – درختی از نشانه ها
- به صورت بازگشتی همه گره هایی که اعلان کلاس هستند را پیدا کنید، سپس برای هر یافت شده:
- گره را بخوانید تا نام کلاس، Extends، Implements را بیابید
- گره سازنده را پیدا کنید و پارامترها را از آن بخوانید
- داده های پیدا شده را به آرایه ای فشار دهید
پس از تکمیل این، ما با آرایه ای از اشیاء داده بازتاب کلاس باقی خواهیم ماند. با استفاده از آن، میتوانیم یک فایل منبع تایپ اسکریپت را با آن ابرداده تولید کنیم و آن را در خود پوشه منبع ذخیره کنیم.
پس از آن، فراداده کلاس آماده استفاده است.
حالا چی؟
در پیاده سازی من، پس از تولید فراداده، نتیجه ذخیره شده آرایه ای از اشیاء زیر است:
fqcn: string; // Fully qualified class name - path and name
name: string; // Class name
ctor: Promise<Constructor> | null; // Constructor for that the class - null if not public
implementsInterfaces: string[]; // Interfaces implemented by the class
extendsClass: string | null; // Parent of the class - null if not extending
constructorParameters: ParameterData[]; // names and types of constructor parameters
constructorVisibility: "public" | "protected" | "private"; // Constructor visibility
میتوانیم ببینیم که همه چیز مورد نیاز برای ایجاد یک DI کارآمد را داریم.
اکنون، با توجه به هر کلاس در فراداده:
- ما چگونه نام آن، بنابراین ما می توانیم آن را با نام در صورت نیاز پیدا کنید
- ما رابطهایی را میشناسیم که پیادهسازی میکند، بنابراین میتوانیم آن را با نام رابط پیدا کنیم
- ما تابع سازنده آن را داریم، بنابراین میتوانیم نمونههایی ایجاد کنیم
- ما می دانیم که سازنده چه پارامترهایی، نام و نام نوع آن را می گیرد
سیم کشی خودکار به صورت بازگشتی انجام می شود، فقط باید نام نوع را بدانیم:
- فراداده کلاس را با نام ارائه شده پیدا کنید
- اگر یافت نشد، فراداده کلاسی که نام ارائه شده را پیاده سازی می کند، پیدا کنید
- برای هر پارامتر سازنده در فراداده:
- سیم کشی خودکار پارامتر بر اساس نام نوع به همان روش (به صورت بازگشتی)
- با آرایه پارامترهای آماده، کلاس را بسازید
- نمونه ساخته شده را برگردانید
این روش به همان اندازه با کلاسها و رابطها کار میکند، بنابراین میتوان از هر دو در نام پارامترها و در حین حل استفاده کرد.
و بس! ما با موفقیت یک DI کار را بدون دکوراتور اجرا کردیم.
البته، جزئیات دیگری نیز وجود دارد که باید در نظر گرفته شود تا DI کاملاً کاربردی باشد، اما اکنون که قسمت دشوار آن انجام شده است، اینها چیزهای آسانی هستند.
اگر به پیادهسازی این رویکرد و استفاده از بسته npm علاقهمند هستید، میتوانید آن را در اینجا بیابید.
سیم کشی خودکار مبارک!