سلام عزیزان با آموزش Context Parameters در Kotlin در خدمت شما عزیزان هستیم .احتمالاً برای شما هم پیش اومده: یه تابع Logger میگیره، ولی خودش حتی یه خط هم لاگ نمیکنه. فقط پاسش میده به تابع بعدی. تابع بعدی هم همین کار رو میکنه، تا بالاخره چند مرحله جلوتر، یه تابع واقعاً ازش استفاده کنه.
تا وقتی فقط یه logger داریم، شاید خیلی به چشم نیاد. ولی کافیه session کاربر و سرویس analytics هم به ماجرا اضافه بشن. کمکم لیست پارامترهای تابعها شلوغ میشه و موقع خوندن کد باید دنبال این بگردیم که هر تابع، خودش به کدوم ورودی نیاز داره و کدوم رو فقط به تابع بعدی تحویل میده.
این دستبهدست شدن وابستگیها همون چیزیه که معمولاً بهش parameter drilling میگن. Context Parameters در کاتلین برای اینجور موقعیتها به کار میان: وابستگی رو توی context اعلام میکنیم و دیگه لازم نیست هر بار که یه تابع رو صدا میزنیم، دوباره پاسش بدیم.
قبل از رفتن سراغ کد، نسخه رو هم مشخص کنیم: این قابلیت در Kotlin 2.2 بهصورت Preview معرفی شد و در Kotlin 2.4 به وضعیت Stable رسید. البته پاس دادن مستقیم context argument و پشتیبانی از callable referenceها هنوز Stable نشدن. مثالهای این مقاله برای Kotlin 2.4 نوشته شدن. تغییرات Kotlin 2.2 و تغییرات Kotlin 2.4
Context Parameters در Kotlin به چه دردی میخورد ؟
بیایم از یه مثال کوچیک شروع کنیم
برای اینکه ماجرا روشن بمونه، فعلاً فقط یه logger داریم:

Context Parameters در Kotlin
مثال رو عمداً ساده نگه داشتیم. اگه به کد داخل تابعها نگاه کنین، فقط recordDashboardVisit از logger استفاده میکنه. دو تابع دیگه فقط اون رو به مقصد میرسونن.
حالا بیایم همین سه تابع رو با Context Parameters بازنویسی کنیم:

به جایی که تابعها رو صدا میزنیم نگاه کنین: دیگه logger رو پاس نمیدیم و فقط userId رو پاس میدیم. البته هر سه تابع هنوز نیازشون به AppLogger رو بالای تعریف خودشون اعلام کردن. این نکته رو یادتون باشه؛ جلوتر برای تصمیم گرفتن درباره استفاده از این قابلیت به کارمون میاد.
حالا برای صدا زدن openDashboard باید یه logger در اختیارش بذاریم:

Context Parameters در Kotlin
داخل بلوک context(logger)، تابعهایی که به AppLogger نیاز دارن میتونن از این logger استفاده کنن. اینجا ممکنه یه نکته از چشممون دور بمونه: فقط تعریف کردن یه متغیر به اسم logger کافی نیست. باید مقدارش رو هم وارد context کنیم؛ همون کاری که این بلوک انجام میده.
تفاوت Context Parameters در کاتلین با Context Receivers چیه؟
اگه قبلاً با Context Receivers کار کرده باشین، احتمالاً این شکل نوشتن رو یادتونه:

اینجا میتونستیم log رو بدون نوشتن logger. صدا بزنیم، چون به تابعها و propertyهای receiver مستقیم دسترسی داشتیم. توی یه مثال کوچیک مشکلی به چشم نمیاد، ولی با چند receiver ممکن بود برای فهمیدن اینکه log مال کدومه، مجبور بشیم سراغ تعریفها بریم.
در Context Parameters در kotlin به وابستگی اسم میدیم و موقع استفاده هم همون اسم رو میاریم:

حالا با دیدن logger.log، همونجا میفهمیم داریم از کدوم logger استفاده میکنیم.
کامپایلر کدوم مقدار رو انتخاب میکنه؟
کامپایلر برای پیدا کردن مقدار مناسب، به نوعش نگاه میکنه. فرض کنین دو logger داریم: یکی برای فایل و یکی برای کنسول. اگه هر دو توی یه سطح از scope باشن و با نوع موردنیاز سازگار باشن، کامپایلر نمیتونه بینشون انتخاب کنه و خطا میده. اسمهای fileLogger و consoleLogger هم کمکی به انتخاب نمیکنن، چون هر دو از نوع AppLogger هستن. مستندات Context Parameters

Context Parameters در Kotlin
اگه برای این عملیات fileLogger رو میخوایم، کافیه همین مقدار رو وارد context کنیم:

Context Parameters در Kotlin
بهتره از همین اول context رو نزدیک به جایی نگه داریم که بهش نیاز داریم. اگه یه محدوده بزرگ با وابستگیهای زیاد بسازیم، بعداً موقع خوندن کد باید وقت بیشتری صرف کنیم تا بفهمیم هر مقدار از کجا اومده.
وابستگی در زمان اجرا کجا میره؟
تا اینجا دیگه logger رو موقع صدا زدن تابعها نمینویسیم، ولی در زمان اجرا هنوز باید به تابع برسه. کامپایلر هنگام کامپایل مشخص میکنه کدوم مقدار رو منتقل کنه؛ برای این کار هم container تازهای ساخته نمیشه که در زمان اجرا دنبال سرویس بگرده.
پس اگه هدفمون سریعتر شدن برنامهست، این تغییر بهخودیخود برنامه رو سریعتر نمیکنه. سودی که توی مثالمون میبینیم، موقع خوندن و نوشتن کده: همون logger به مقصد میرسه، بدون اینکه هر بار که یه تابع رو صدا میزنیم، تکرارش کنیم.
مقالات مرتبط : کاتلین کوروتین چیست و دارای چه ویژگی هایی میباشد ؟
اگه خود تابع از logger استفاده نکنه
برگردیم به loadDashboard. این تابع خودش از logger استفاده نمیکنه، پس حتی میتونیم بهجای اسم پارامتر، _ بذاریم:

با این کار، logger هنوز در دسترس recordDashboardVisit هست، فقط اسمی نداره که مستقیم بهش اشاره کنیم. اگه بعداً لازم شد مقدارش رو بگیریم، contextOf<AppLogger>() در دسترسه:

البته توی همین مثال دوم، بهتره دوباره اسم logger رو به پارامتر بدیم؛ خوندنش راحتتره. _ بیشتر وقتی به درد میخوره که خود تابع از logger استفاده نمیکنه و فقط تابع بعدی بهش نیاز داره.
مقالات مرتبط : برنامه نویسی شی گرا چیست و چه کاربرد هایی دارد ؟
پاس دادن مستقیم context argument به چه شکلی است
گاهی فقط میخوایم یه تابع رو صدا بزنیم و ترجیح میدیم مقدار context رو همونجا پاس بدیم. در Kotlin 2.4 این امکان وجود داره، ولی هنوز Experimental هست. برای استفاده باید این گزینه رو فعال کنیم:

Context Parameters در کاتلین
بعد میتونیم تابع رو اینطوری صدا بزنیم:

Context Parameters در کاتلین
وقتی فقط یه تابع رو صدا میزنیم، این شکل جمعوجورتره. ولی اگه چند تابع قراره از همون وابستگی استفاده کنن، بلوک context(...) کمک میکنه محدوده مشترکشون رو یهجا ببینیم. پاس دادن مستقیم آرگومان وقتی هم به کار میاد که چند تابع همنام یا overload داریم و تفاوتشون توی context parameter هست؛ با این کار مشخص میکنیم کدوم رو میخوایم صدا بزنیم. Explicit context arguments در Kotlin 2.4
آیا Context Parameters در Kotlin میتواند جایگزین CompositionLocal در Compose شود؟
اگه با Compose کار میکنین، احتمالاً به این فکر کردین که میشه theme یا وابستگیهای مشترک صفحه رو هم از همین راه منتقل کرد. اینجا باید ببینیم وقتی مقدار عوض میشه چه اتفاقی میافته: Context Parameters در Kotlin بهتنهایی تغییرات رو مشاهده نمیکنن و مسئول مدیریت recomposition نیستن.
اگه آبجکتی که از context میگیریم یه State<T> داشته باشه، خوندن اون state داخل composition همچنان طبق قواعد Compose عمل میکنه. یعنی اینکه آبجکت رو از context گرفتیم، رفتار state رو عوض نمیکنه. State در Compose
قبل از تغییر کد، بهتره ببینیم دقیقاً چی اذیتمون میکنه. اگه مسئله تکرار آرگومانهاست، Context Parameters ارزش بررسی دارن. اگه مسئله اینه که با تغییر theme کدوم بخشهای صفحه بهروز بشن، هنوز باید سراغ رفتار state و composition بریم. مشترک بودن یه مقدار بین چند composable، بهتنهایی دلیل کافی برای کنار گذاشتن CompositionLocal نیست.
کجا ازش استفاده کنیم؟
اگه بخوایم این قابلیت رو وارد پروژه کنیم، لازم نیست یهباره همه پارامترها رو جابهجا کنیم. برای شروع میتونیم اینطوری انتخاب کنیم:
حتماً. این نسخه برای **RTL و نمایش مرتب در وردپرس** اصلاح شده؛ عرض ثابت و `height` هم حذف شده تا روی موبایل و دسکتاپ بهتر نمایش داده شود:
حتماً. این نسخه برای **RTL و نمایش مرتب در وردپرس** اصلاح شده؛ عرض ثابت و `height` هم حذف شده تا روی موبایل و دسکتاپ بهتر نمایش داده شود:
| نوع ورودی یا وابستگی | انتخاب اولیه |
|---|---|
| userId، مبلغ یا متن جستوجو که تابع قراره باهاش کار کنه | پارامتر معمولی |
| repository یا سرویس ثابت موردنیاز یه کلاس | Constructor Injection |
| logger یا transaction که چند تابع پشت سر هم بهش نیاز دارن | بررسی Context Parameters |
| مقادیر UI که تغییرشون باید در Composition دنبال بشه | بررسی State و ابزارهای Compose براساس نحوه مصرف |
چند محدودیت رو هم باید در نظر داشته باشیم: constructor نمیتونه Context Parameter تعریف کنه. برای propertyهایی که Context Parameter دارن هم initializer، backing field و delegation پشتیبانی نمیشن. محدودیتهای فعلی
برای امتحان کردنش، از همون جایی شروع کنین که یه logger یا وابستگی مشابه، چند تابع رو بدون استفاده پشت سر میذاره. یه مسیر کوچیک رو تغییر بدین و دوباره کد رو بخونین. راحتتر میفهمیم هر تابع کدوم تابع رو صدا میزنه؟ هنوز معلومه logger از کجا میاد؟ جواب این دو سؤال کمک میکنه بفهمین Context Parameters در Kotlin توی اون بخش از پروژه واقعاً به کارتون اومدن یا فقط شکل نوشتن کد رو عوض کردن.


