آموزش توابع scope در کاتلین : let ، run ، with ، also و apply

s

توابع استاندارد یکی از ویژگیهای جالب و عالی جدید کاتلین هست که به کوتاه تر شدن و خواناتر شدن کدها خیلی کمک می کنن. اگه با این ویژگی آشنایی ندارید لطفا تا آخر این مقال همراه ما باشید.

پیش نیاز این آموزش یاد داشتن extension function ها هست، میتونید از اینجا extension functionها رو یاد بگیرید.

خیلی از توابع استاندارد کاتلین خیلی شبیه به هم هستن، – همونطور که کفگیر و ملاقه‌های بالا شبیه به هم هستن 🙂 ولی هر کدوم یک استفاده‌ای دارن – به همین خاطر گاهی اوقات مردد می‌مونیم که از کدوم یکی استفاده کنیم. توی این مقاله یه روش ساده برای اینکه فرق بین اونا رو تشخیص بدیم و بفهمیم از کدوم یکی استفاده کنیم معرفی می‌کنم. فعلا به متدهای پایین توجه نکنید بعدا بیشتر در موردشون توضیح میدم.

متد run

https://gist.github.com/sajjadyousefnia/a742d2b7be22679e0d833ea31c221d7c

متد T.run

https://gist.github.com/sajjadyousefnia/bcee9d0db82253f80912897a0f6b0e43

متد with

https://gist.github.com/sajjadyousefnia/7f85da7083f03d7a8807e779140b87ae

متد T.apply

https://gist.github.com/sajjadyousefnia/65a7d3f514a96661ca44b024943d63fd

متد T.also

https://gist.github.com/sajjadyousefnia/94f68f66cddd946842f92519b2c6b05d

متد T.let

https://gist.github.com/sajjadyousefnia/10d9f342f0b4a93014b30d541b55172b

ا Scoping Functions

توابعی که می‌خوام در مورد اونا صحبت کنم ، T.run ، T.let ، T.also و T.apply هستن. که من به اونا Scoping Functions ( توابع قلمرو ) میگم، به این خاطر که وظیفه‌ی اصلی اونا اینه که توی تابع به ما توانایی به وجود آوردن قلمرو – اسکوپ – کوچکتری رو داخل بدنالبته یه نکته رو عرض کنم که معمولا مرز قلمروها رو با { و } مشخص می‌کنن .

ساده ترین راه برای نشون دادن مسئله، استفاده از تابع run هست.

https://gist.github.com/sajjadyousefnia/dc424bd4bc992348bc774682c5b9b42c

داخل تابع test با انجام این کار تونستیم یک اسکوپ مجزا به وجود بیاریم.داخل اسکوپ run، قبل از چاپ متغییر mood ، مقدارش رو برابر با I am happy قرار دادیم.

شاید این Scoping Functions توی نگاه اول زیاد به درد بخور به نظر نرسه، ولی یه قابلیت مهم دیگه ای هم داره، و اون اینه که میتونه چیزی رو return کنه.

کد پایینی که از این قابلیت استفاده کرده خیلی شسته رُفته‌تر شده، و دیگه نیازی نیست که برای انجام کار موردنظرمون دوبار از متد show استفاده کنیم.

https://gist.github.com/sajjadyousefnia/a85a3398b85145d50e6272cf1f7cb037

انواع Scoping functions

حالا برای این که Scoping Function ها رو بهتر متوجه بشیم، می‌تونیم به سه روش زیر اونا رو دسته بندی کنیم. و با سه ملاک از هم دیگه تمییزشون بدیم.

۱- تابع معمولی یا extension function

توابع with و T.run توی نگاه اول خیلی شبیه هم هستن. همونطور که پایین می‌بینیم، دارن کار یکسانی رو انجام میدن.

https://gist.github.com/sajjadyousefnia/04d8eaeab80ab11586189eb572bfd182

اما در حقیقت کمی متفاوت هستن، و فرقشون اینه که with یک تابع معمولی هست، در حالی که T.run یک extension function هست.( حتی اگه به شکل extension function به کار نره )

به متدهای اونا که اول مقاله نوشته شده، توجه کنید تا بهتر متوجه بشید.

خب حالا یه سوالی پیش میاد و اون اینکه مزیت هر کدوم چیه؟

حالا تصور کنید که احتمال این باشه که webview.settings تهی یا همون null باشه، حالا اون توابع به شکل پایینی درمیان.

https://gist.github.com/sajjadyousefnia/307530313c346b721b16ae69e9fbdcb7

خب همونطور که دیدین حتما متوجه شدین که توی این مورد استفاده از T.run کار بهتری به نظر بهتر میرسه، چون قبل از اینکه بخوایم ازش استفاده کنیم، میتونیم بررسی کنیم که تهی هست یا نه؟

۲- آرگومان this و آرگومان it

خب، اول باید بررسی کنیم که this و it هر کدوم چی هستن و کجا استفاده میشن؟

آرگومان this

از this برای نشون دادن receiver یا همون آبجکت دریافت کننده‌ی فعلی استفاده میکنیممنظور از دریافت کردن، دریافت کردن this هست و در دو جا برابر با دو چیز تقریبا مختلف هست :

۱- توی یک کلاس، this یعنی آبجکت فعلی که از جنس این کلاس ساخته شده.

۲- توی یک extension function یا یک function literals with reciever که از ترکیب لامبدا و extension function ساخته میشن this عبارتست از آخرین پارامتر دریافت کننده‌ای که مربوط به سمت چپ نقطه هست و از جنس عبارت سمت چپ نقطه هم هست،

اگه به مثال توجه کنید بهتر متوجه منظورم میشید:

https://gist.github.com/sajjadyousefnia/f4d100c3ff7dc29499fb031f47305a9c

توی extension function بالا متغییر ، عبارت adult چاپ میشه. چون همونطور که گفتم، این متغییر هست که به عبارت سمت چپ نقطه فرستاده شده. برای خسته کننده نبودن، this توی function literal ها رو دیگه توضیح نمیدم و اینجا هم نیازی به function literal نداریم.

آرگومان it

خیلی پیش میاد که یک لامبدا داشته باشیم که فقط یک پارامتر داشته باشه.

در این صورت اگر که کامپایلر بتونه امضا یا همون پارامتر رو تشخیص بده، میتونیم فلش لامبدا رو حذف کنیم و به جای استفاده از اسم پارامتر، از it استفاده کنیم.

https://gist.github.com/sajjadyousefnia/2e5e06495aa683d96be81caf10b0de33

خب، حالا اگه به متدهای ابتدای مقاله نگاهی بندازید حتما متوجه میشید که چه جاهایی از this و چه جاهایی از it به عنوان آرگومان و پارامتر استفاده میشه.
نکته : اگه کلاسهای تودرتو داشته باشیم، this برابر با داخلی‌ترین آبجکت توی کلاس هست.

https://gist.github.com/sajjadyousefnia/0bf0868731861bcfe4340528e2b5b3ac

اگه یه نگاهی به توابع T.run و T.let بندازیم، حتما متوجه میشید که همه‌ی ویژگی هاشون به جز یکی یکسانه، در واقع، اون ویژگی ای که باعث تمایز این دوتا میشه، نوع آرگومانی که دریافت میکنن هست. اگه به کد پایینی نگاه کنید می‌بینید که ظاهرا هر دو منطق کاملا یکسانی دارن،البته در ادامه بیشتر توضیح میدم.

https://gist.github.com/sajjadyousefnia/78ee6ebf71853a09ed129a178c8d4703

اگه signature یا همون پارامتر تابع T.run رو بررسی کنید، حتما متوجه این میشید که با استفاده از ().block :T داره صرفا یک Extension Function رو ایجاد میکنه. به همین دلیل هست که T داخل قلمروی مورد نظر میتونه با استفاده از this فراخوانی و استفاده بشه. توی کدنویسی هم که اکثر اوقات میشه this رو حذف کرد. به همین خاطر توی عبارت بالا به جای میشه به جای ${this.length} داخل println فقط $length رو قرار داد. که من اسم این کار رو this as argument ( استفاده از this به عنوان آرگومان ) میگذارم.

اگه به T.let توجه کنید میبینید که یک آرگومانی به شکل block:T داره به تابع فرستاده میشه. برای همینه که میشه داخل let ، اون رو با استفاده از it فراخوانی کرد و مورد استفاده قرار داد. که من اسم این قابلیت رو it as argument میگذارم.

ممکنه که اینجوری نتیجه‌گیری کرده باشید که چون ما میتونیم توی T.run اسم پارامترمون رو نبریم، حتی از this هم میشه استفاده نکنیم، ولی توی T.let در خلاصه‌ترین حالت ممکن باید از it استفاده کنیم (یا به قولی T.run از اون یکی implicit تره) ——> پس حتما استفاده از T.run بهتر خواهد بود. در حالی که اینطور نیست! به دلایل زیر استفاده از T.let بهتره :

  • توی T.let بهتر میشه این مسئله رو متوجه شد که این پارامتری که می‌بینیم، عایا مال این کلاس ( تابع یا property ) هست یا (تابع یا property ) کلاس دیگه.( مثلا در حالتی که کلاسهای تودرتو داشته باشیم )
  • توی eventهایی که this قابل حذف شدن نیست ( مثلا فرض کنید که داخل همون اسکوپ let یا run بخوایم اون متغییر رو به عنوان پارامتر یک متد مورد استفاده قرار بدیم )، استفاده از it به جای this به نظر بهتر و کوتاه‌تر میرسه.
  • به لحاظ نام‌گذاری و اینا استفاده از it بهتره، چون می‌تونیم به جای it ، داخل اسکوپ داخلی از یک اسم دیگه براش استفاده کنیم. به مثال زیر توجه کنید :
https://gist.github.com/sajjadyousefnia/c975a52c5acbdd88136ab80e2fcd93c5

۳- جنس چیزی که return میشه از نوع this باشه یا از جنس دیگری

حالا یک نگاهی به T.let و T.also میندازیم. اگه به اسکوپ داخلی تابع نگاهی بندازیم، ظاهرا هر دو یک کار رو انجام میدن.

https://gist.github.com/sajjadyousefnia/1a55699e16e78f60a4c97352c3e5e005

اما کمی با هم متفاوتن، این تفاوت در چیزی که return میکنن هست. T.let یک مقدار از هر نوعی رو میتونه return کنه ولی T.also صرفا متغییری از نوع T رو return میکنه.

هر دوی این توابع زنجیره‌ای به درد بخور هستن و کاربرد زیادی توی توابع زنجیره‌ای دارن، منتها بایستی که هر کدوم توی توابع زنجیره‌ای به شیوه‌ی درستی مورد استفاده قرار بگیرن. چون در T.let ، متغییر هربار دچار تغییر میشه ولی توی همه‌ی T.also ها از یک this یکسان استفاده میشه، به مثال‌های زیر توجه کنید :

https://gist.github.com/sajjadyousefnia/6ca462ef321b8c2383db10d88fa525cc

اینطور به نظر میرسه که بهتره به جای زنجیری نوشتن T.also همه رو داخل یک تابع بنویسیم، ولی اگه عمیق‌تر فکر کنیم متوجه میشیم که این حالت زنجیری سه مزیت داره :

۱- باعث میشه که بهتر بتونیم یک پروسه رو به قسمت‌های کوچکتر تقسیم کنیم.

۲- باعث میشه که بتونیم قبل از خروجی، داده‌ها رو بهتر مدیریت کنیم و احتمال خطا کاهش پیدا میکنه.

با ترکیب زنجیری متدهای مختلف میتونیم یک تابع قدرتمندتر و در عین حال کوتاه‌تر داشته باشیم. مثل پایینی :

https://gist.github.com/sajjadyousefnia/115c5228b3433904d2d23e7ed01a7567

با استفاده از سه ویژگی‌ای که گفته شد، میشه عملکرد یک تابع اسکوپ رو به خوبی فهمید. در ضمن ویژگی‌های ‌T.apply رو که قبل نگفتم به صورت زیر هست :

۱- یک extension function هست.

۲- این متد، this رو به عنوان آرگومان میفرسته.

۳-این متد this رو return میکنه.

ک میتونیم ازش به صورت زیر استفاده کنیم :

https://gist.github.com/sajjadyousefnia/42aab8f7b39667e8e5e1cf65c15b6ea1

یا میتونیم با استفاده ازش یک تابع رو به شکل زنجیری دربیاریم.

https://gist.github.com/sajjadyousefnia/c313a684bd679b7b235b396fdd02dc00

انتخاب تابع مناسب

کاملا واضحه که با استفاده از این سه ویژگی میتونیم توابع رو به سه دسته تقسیم‌بندی کنیم. و براساس همون درختی که ایجاد کردیم تصمیم بگیریم که کِی و از کدوم یکی استفاده کنیم و کدوم یکی به کار ما میاد.

امیدوارم خوب نوشته باشم، اگه دوست داشتید نظرتون رو بفرمایید.

نوشته آموزش توابع scope در کاتلین : let ، run ، with ، also و apply اولین بار در ویرگول پدیدار شد.

گردآوری توسط ایده طلایی

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *