۲۲ اکستنشن کاتلین برای نوشتن کد تمیز | به زبان ساده


یکی از دلایل محبوبیت گستردهتر زبان کاتلین (Kotlin) در میان توسعهدهندگان موبایل، پشتیبانی آن از اکستنشنها است. اکستنشنها به برنامهنویس امکان میدهند که متدها را به هر کلاس موجود و یا حتی به نوع Any و Optional (مانند ?Int) اضافه کند. در این مقاله با 22 اکستنشن کاتلین برای نوشتن کد تمیز آشنا خواهیم شد.
اگر یک کلاس مبنا را بسط دهید، همه کلاسهای مشتقشده به صورت خودکار این اکستنشن را دریافت میکنند. همچنین میتوانید برخی متدها را از اکستنشنها override کنید که موجب میشود این مکانیسم قدرت و انعطافپذیری هر چه بیشتری پیدا کند.
در این مقاله از کاتلین 1.4.0 برای اندروید در اندروید استودیو نسخه 4.0.1 استفاده شده است. همه متدهای معرفی شده مربوط به کاتلین و نه جاوا هستند. با این حال غالب اکستنشنها در نسخههای دیگر کاتلین و حتی برخی از آنها در محیطهای دیگر نیز کار میکنند.
()Int.toDate و Int.asDate
در اغلب موارد نیاز داریم که تاریخ و زمان را به صورت یک timestamp دریافت کنیم. منظور از timestamp تعداد ثانیههایی (گاهی میلیثانیهها) است که از 1 ژانویه 1970 سپری شده است. این زمان در علم رایانه epoch نامیده میشود.
این اکستنشن ساده ثانیهها را به یک شیء Date تبدیل میکند. این اکستنشن دو گزینه به صورت مشخصههای function و یک read-only دارد. این دو از نظر تابعی یکسان هستند و این که از کدام یک استفاده میکنید به سلیقه شما بستگی دارد.
کاربرد
نکته: در این مثال از یک اکستنشن دیگر به نام getIntOrNull استفاده شده است. این اکستنشن در صورتی که JSON وجود داشته باشد یک مقدار int و در غیر این صورت null بازگشت میدهد.
(…)String.toDate و (…)Date.toString
یک تبدیل رایج دیگر برای شیء Date این است که آن را به یک رشته و یا از یک رشته تبدیل کنیم. توجه کنید که در مورد متد استاندارد toString() در جاوا/کاتلین صحبت نمیکنیم. در این مورد باید یک قالب را تعیین کنیم:
هشدار عملکردی: در این مثال ما هر بار یک شیء aSimpleDateFormat ایجاد میکنیم. اگر از این اکستنشن درون یک لیست استفاده میکنید، یا یک پاسخ بزرگ را از یک API تحلیل میکنید، باید کد زیر را از اکستنشن حذف کنید و آن را درون یک متغیر گلوبال یا یک عضو کلاس استاتیک قرار دهید:
theval dateFormatter = SimpleDateFormat(format, Locale.US)
کاربرد
()Int.centsToDollars و ()Int.centsToDollarsFormat
اگر با قیمتها کار میکنید، ممکن است در زمان کار با انواع داده Float و Double با مشکل دقت اعشار مواجه شوید. یک روش رایج برای نمایش قیمت استفاده از مقادیر Int است. اما به جای ذخیره کردن مقادیر در وجه اصلی (مانند دلار یا یورو) میتوانید آنها را به صورت کمترین واحدهای پولی (یعنی سنت) نمایش دهید.
به این منظور نیاز به محاسباتی درون Int دارید و در نهایت تنها مقدار نهایی را به کاربر نمایش میدهید یعنی Double را مستقیماً به String تبدیل میکنید.
کاربرد
()Double.toPrice
اکستنشن مفید دیگری که در زمان کار با نرخها به کار میآید مربوط به قالببندی اعداد و جداسازی ارقام است. در اغلب موارد درون یک اپلیکیشن، تنها یک مجموعه از قواعد برای قالببندی قیمت داریم. در این حالت میتوان از یک اکستنشن در کل اپلیکیشن برای نمایش نرخها استفاده کرد.
در اکستنشن زیر از دو رقم کسری استفاده شده و یک کاما بین ارقام عدد نمایش مییابد و عدد را سه رقم به سه رقم با نقطه جدا میکند تا راحتتر خوانده شود. این بار از نوع ()Double.toPrice استفاده خواهیم کرد. تبدیل Int به Double با استفاده از اکستنشن ()centsToDollars که در بخش قبل معرفی کردیم، ممکن است.
کاربرد
(…)String.toLocation
زمانی که با یک API کار میکنید تا مختصات جغرافیایی یک شیء را دریافت کنید، ممکن است این مختصات را با صورت دو فیلد مجزا دریافت کنید. اما گاهی اوقات تنها یک فیلد دریافت میشود که مقادیر طول و عرض جغرافیایی درون آن با کاما از هم جدا شدهاند.
اکستنشن زیر این نوع از مختصات جغرافیایی را به موقعیت اندروید در یک خط تبدیل میکند:
کاربرد
به طور مشابه، میتوانید یک اکستنشن برای تبدیل String به LatLng از پکیج Google Maps بسازید. در این حالت، حتی نیازی به تعیین location provider نیز وجود ندارد.
String.containsOnlyDigits و String.isAlphanumeric
در این بخش در مورد مشخصههای یک String صحبت میکنیم. رشتهها میتوانند خالی یا غیر خالی باشند. برای نمونه ممکن است شامل تنها رقم و یا تنها کاراکترهای عددی-رقمی باشند. اکستنشنهای زیر به ما امکان میدهند که یک رشته را در یک خط از کد اعتبارسنجی کنیم:
کاربرد
Context.versionNumber و Context.versionCode
در اپلیکیشنهای اندروید معمولاً بهتر است که شماره نسخه اپلیکیشن را در صفحه about یا support نمایش دهیم. این کار به کاربران کمک میکند تا از بهروز بودن اپلیکیشن خود مطمئن شوند و یا اطلاعات ارزشمندی در زمان معرفی باگ ارائه نمایند. تابعهای استاندارد اندروید برای اجرای این کار نیازمند چندین خط از کد و مدیریت استثناها هستند. اکستنشن زیر به ما امکان میدهد که شماره نسخه را در یک خط از کد دریافت کنیم:
کاربرد
Context.screenSize
یک اکستنشن مفید دیگر در اندروید اکستنشن Context نام دارد که به ما امکان میدهد تا اندازه صفحه دستگاه را دریافت کنیم. این اکستنشن اندازه صفحه را با مقیاس پیکسل بازگشت میدهد.
کاربرد
Any.deviceName
این که نوع Any را بسط دهیم موضوع بحثبرانگیزی محسوب میشود. اگر چنین کنیم، تابع به صورت گلوبال طاهر میشود و از این رو اساساً معادل همان اعلان گلوبال تابع است. در اکستنشن زیر نام دستگاه اندرویدی را دریافت میکنیم. از آنجا که این کار نیازی به هیچ Any.deviceName ندارد، یک اکستنشن Any استفاده شده است:
کاربرد
T.weak
موقعیتی که در این بخش مطرح میکنیم کمی پیچیده است. فرض کنید یک Activity و یک ListView داریم که درون خود خانههای زیادی دارد. هر خانه میتواند نوعی بازخورد ارائه کند. فرض کنید یک اینترفیس delegate داریم و خود اکتیویتی را به یک خانه ارسال میکنیم، زیرا اینترفیس خانه را پیادهسازی کرده است:
ما در اینجا فقط ساختار را نشان دادهایم. در یک اپلیکیشن واقعی میتوانیم از ViewHolder و استفاده مجدد از خانهها بهره بگیریم که کار صحیح نیز همین است.
یکی از مشکلات این حالت آن است که Act و Cell ارجاعهایی به همدیگر دارند که منجر به نشت حافظه میشود. یک راهحل خوب در اینجا آن است که از WeakReference استفاده کنیم. متغیرهای Delegate که درون یک WeakReference قرار گیرند تأثیری روی شمارنده ارجاع در Act نخواهند داشت و از این رو به محض بسته شدن صفحه به همراه همه خانههای تخصیصیافته تخریب خواهند شد.
این اکستنشن ساده به ما امکان میدهد که یک ارجاع ضعیف را با افزودن.weak به هر شیئی به دست آوریم:
کاربرد
باید تأکید کرد که این اکستنشن ژنریک است و با هر نوع دادهای کار میکند.
(…)Context.directionsTo
باز کردن ناوبری از هر اپلیکیشن اندروید یک قابلیت رایج است. اندروید همانند Google Maps یک محصول گوگل است. اپلیکیشن Google Maps از پیش روی اغلب گوشیها و تبلتهای اندروید نصب شده است. آسانترین راهکار برای باز کردن ناوبری در یک اپلیکیشن اندروید، باز کردن اپلیکیشن Android Maps است. اگر این اپلیکیشن نصب نشده باشد، این لینک در یک مرورگر باز خواهد شد.
این یک اکستنشن Context است و از دیدگاه کدنویسی مشکلی ندارد. اما از دیدگاه منطقی باید آن را مشخصتر سازیم. این اکستنشن میتواند Activity یا AppCompatActivity را بسط دهد تا از برخی عوارض جانبی مانند استفاده از آن درون یک Service جلوگیری شود. شما میتوانید کلاس قابل بسط را به هر چیزی که در اپلیکیشن خود استفاده میکنید، تغییر دهید.
(…)AppCompatActivity.callTo یا (…)Activity.callTo
در این اکستنشن هم از همان منطق اکستنشن قبلی استفاده کردهایم. اما به جای ناوبری به یک شیء تلاش میکنیم تا یک تماس برقرار کنیم. این دو اکستنشن میتوانند در کنار هم در یک اپلیکیشن مورد استفاده قرار گیرند.
پیچیدگی این راهحل در مسئله مجوز (permission) است. اندروید برای ایجاد یک تماس نیازمند کسب مجوز است. اما برخلاف آیفون این مجوز نه برای برقرار تماس؛ بلکه برای باز کردن صفحه تماسها دریافت میشود.
این اکستنشن دو پارامتر دارد و به صورت مستقیم یک Activity یا کلاس مشابه را بسط میدهد. پارامتر اول یک شماره تلفن است و پارامتر دوم نیز کد درخواست است. در صورتی که نیاز به مجوز داشته باشیم، اما آن را کسب نکرده باشیم به صورت زیر عمل میکنیم:
کاربرد
توجه کنید که کد زیر بخشی از AppCompatActivity است
String.asUri
ما بر حسب عادت نشانیهای اینترنتی را به صورت یک رشته تصور کنیم، چون میتوانیم نشانی را تایپ کرده و درون گیومه قرار دهیم. اما اندروید برای کاربرد درونی خود یک نوع خاص برای نشانیهای اینترنتی به نام Uri دارد. تبدیل Uri به رشته و برعکس کار آسانی است. اکستنشن زیر به ما این امکان را میدهد که یک string را به یک Uri تبدیل کنیم و این کار را با تأیید صحت کار انجام دهیم. یک Uri معتبر باشند، مقدار null بازگشت مییابد:
کاربرد
(…)Uri.open(…) ،Uri.openInside و (…)Uri.openOutside
گاهی اوقات زمانی که یک Uri داریم، میخواهیم آن را در یک مرورگر باز کنیم. دو روش برای انجام این کار وجود دارد:
- آن را درون اپلیکیشن باز کنیم.
- آن را در یک مرورگر بیرونی باز کنیم.
ما معمولاً علاقه داریم که کاربر را درون اپلیکیشن حفظ کنیم، اما برخی اسکیماها امکان باز شدن درون اپلیکیشن را ندارند. برای نمونه ما نمیخواهیم //:instagram را درون مرورگر اپلیکیشن باز کنیم. در واقع ما تنها میخواهیم نشانیهای //:http و //:https را درون اپلیکیشن خود باز کنیم.
بدین ترتیب سه اکستنشن مختلف اضافه میکنیم. یکی برای باز کردن Uri درون اپلیکیشن، دیگری برای باز کردن در مرورگر بیرونی و آخری برای تصمیمگیری به صورت دینامیک بر اساس نوع اسکیمای نشانی مورد استفاده قرار میگیرد.
برای باز کردن یک صفحه وب درون یک اپلیکیشن باید یا یک اکتیویتی مجزا ایجاد کنیم و یا از یک کتابخانه استفاده کنیم که این کار را برای ما انجام میدهد. برای سادگی کار از روش دوم استفاده میکنیم و کتابخانه FinestWebView را ایمپورت میکنیم.
در فایل گریدل اپلیکیشن خط زیر را بنویسید:
و در مانیفست نیز کد زیر را درج کنید:
اکستنشنهایی که اشاره کردیم به صورت زیر هستند:
کاربرد
(…)Context.vibrate
گاهی اوقات میخواهیم نوعی بازخورد فیزیکی از گوشی دریافت کنیم. برای نمونه ممکن است بخواهیم زمانی که کاربر روی یک دکمه ضربه میزند، گوشی به لرزش دربیاید. بحث در مورد این که آیا این یک رویه مناسب است یا نه را به جای دیگری موکول میکنیم و روی پیادهسازی آن متمرکز میشویم. قبل از هر چیز باید این مجوز را به مانیفست اضافه کنیم:
اکستنشن
کاربرد
یا
سخن پایانی
امیدواریم اکستنشنهایی که در این مقاله معرفی کردیم، برای شما مفید بوده باشد و موجب کوتاهتر و تمیزتر شدن کدهایتان شود. شما میتوانید بسته به الزامات و نیازهایتان این اکستنشنها را تغییر داده و در پروژههای خود مورد استفاده قرار دهید. توجه کنید که باید مجوزهای لازم را در مانیفست اپلیکیشن اضافه کنید.