تابع اکستنشن در کاتلین — به زبان ساده
زبان کاتلین از امکان بسط یک کلاس با کارکرد جدید بدون پیادهسازی مفهوم وراثت یا بهرهگیری از یک الگوی طراحی مانند «دکوراتور» (Decorator) پشتیبانی میکند. این کار از طریق خاصی که «تابع اکستنشن» (Extension Function) نام دارد انجام مییابد. از این رو این ظرفیت میتواند برای تمیزتر شدن و سهولت خوانایی کد مؤثر باشد و حجم آن را نیز کاهش دهد. در این مقاله تلاش میکنیم تابع اکستنشن در کاتلین را به عنوان یک امکان برای تحقق رویههای مناسب توسعه اندروید مورد بررسی قرار دهیم.
تابع اکستنشن چیست؟
تابع اکستنشن اساساً یک عضو تابعی از یک کلاس است که خارج از کلاس تعریف میشود. برای نمونه اگر لازم باشد از یک متد کلاس String استفاده کنیم که رشته جدیدی را با حذف کاراکتر اول و آخر بازگشت دهد، میتوانیم یک متد اکستنشن برای آن بنویسیم. در واقع این متد در کلاس String از قبل وجود ندارد.
مزیت استفاده از تابع اکستنشن
به طور کلی، تابع اکستنشن این قابلیت را دارد که کد را خلاصهتر و خواناتر ساخته و از طریق حذف کد «آماده» (Boilerplate) از پروژه آن را منطقیتر بسازد. علاوه بر آن کد کمتر به این معنی است که فرصتهای کمتر برای ایجاد خطا پدید میآید. افزون بر دلایل فوق، موارد زیر را نیز میتوان برشمرد:
- میتوان به عنوان یک کارکرد به کلاس نهایی (Final) اضافه کرد. یعنی لازم نیست کلاس به صورت کلاس غیر نهایی تعریف شود.
- میتوان کارکردی را بدون انشعاب کلاس فرعی اضافه کرد. یعنی ارجاع شیء و پیادهسازی را میتوان در کلاس مبنا ارائه کرد.
- امکان تحقق برخی رویههای مناسب برنامهنویسی شیءگرا به خصوص اصل باز-بستن (Open–closed) از سری اصول SOLID را ممکن میسازد که در آن نهادهای مشخصشده (کلاسها، ماژولها، تابعها و غیره) باید برای اکستنشن (بسط یافتن) باز شوند، اما برای تغییر یافتن بسته باشند.
تابع اکستنشن کاتلین
تابع اکستنشن کاتلین اساساً امکانی برای افزون متدها به کلاس فراهم میسازد که در آن نیازی به وراثت یک کلاس یا استفاده از هر نوع الگوی طراحی وجود ندارد. تابع اکستنشن ایجاد شده به عنوان یک تابع معمولی درون کلاس مورد استفاده قرار میگیرد. تابع اکستنشن با استفاده از یک نوع دریافتکننده پیشوند (نام کلاس) و همچنین استفاده از نام متد (تابع اکستنشن) به صورت زیر اعلان میشود:
1fun <class_name>.<method_name>()
به طور کلی، میتوانیم همه متدها را از خارج از کلاسی که هم اینک درون یک کلاس تعریف شده است، فراخوانی کنیم. برای نمونه فرض کنید که میخواهیم یک متد به نام ()perimeter مربوط به کلاس Circle را فراخوانی کنیم که در این کلاس تعریف نشده است. این تابع تعیین شده ()Circle.perimeter به نام تابع اکستنشن خوانده میشود و کلاس Circle به عنوان نوع دریافتکننده آن شناخته میشود.
1class Circle(val radius: Double){
2
3 fun area(): Double{
4 return Math.PI * radius * radius;
5 }
6}
7fun Circle.perimeter(): Double{
8 return 2 * Math.PI * radius;
9 }
10fun main(args: Array<String>){
11
12 val circle = Circle(5.5);
13 val perimeterValue = circle.perimeter()
14 println("Perimeter: $perimeterValue")
15 val areaValue = circle.area()
16 println("Area: $areaValue")
17}
کلاسهای بسط یافته کتابخانه
در کاتلین میتوانید کلاسهای کتابخانه را نیز علاوه بر کلاسهای تعریف شده از سوی کاربر بسط دهید. در واقع، تابع اکستنشن را میتوان به کلاسهای کتابخانه نیز اضافه کرد و به روشی مشابه کلاسهای تعریف شده از سوی کاربر مورد استفاده قرار داد. به مثال زیر توجه کنید:
1fun main(args: Array<String>){
2
3 fun Int.abs() : Int{
4
5 return if(this < 0)
6 }
7
8 println((-4).abs())
9 println(4.abs())
10}
دریافتکننده تهیپذیر
تابع اکستنشن را میتوان با نوعی کلاس تعریف کرد که «تهیپذیر» (Nullable) است. در چنین موقعیتی بررسی تهی بودن درون تابع اکستنشن اضافه میشود و مقدار مناسب بازگشت مییابد. برای نمونه عناصر Mutable List را با استفاده از متد ()swap تعویض میکنیم، اما کلاس MutableList به صورت درونی از متد swap() پشتیبانی نمیکند. از این رو برای حل این مشکل، باید از طریق تابع ()swap یک تابع اکستنشن برای MutableList بنویسیم.
1funMutableList<Int>?.swap(first: Int, second: Int): Any{
2
3 if (this == null) return "null" //Checked the null-ability here!
4
5 else{
6 val temp = this[first]
7 this[first] = this[second]
8 this[second] = temp
9 return this
10 }
11}
12fun main(args: Array<String>){
13 val list = mutableListOf(6,9,7)
14 println("Print the list :$list")
15val result = list.swap(0, 2)
16 println("swapping the list :$result") //Output: [7, 10, 6]
17}
اکستنشنهای شیء companion
یک شیء companion به شیئی گفته میشود که درون یک کلاس مورد اشاره قرار میگیرد و با کلیدواژه companion مشخص میشود. شیء companion برای فراخوانی مستقیم تابع عضو کلاس با استفاده از نام کلاس و به روشی مشابه کلیدواژه static مورد استفاده قرار میگیرد. اگر یک کلاس شامل شیء companion باشد، میتوانیم تابع اکستنشن و مشخصههایی برای آن شیء companion تعریف کنیم.
1class SampleClass{
2
3 companion object{
4 fun display():String{
5 return "Companion Object Extensions"
6 }
7 }
8}
9
10fun main(args: Array<String>){
11
12 val instance = SampleClass.display()
13
14}
به علاوه، اکستنشن شیء companion به وسیله نام کلاس فراخوانی میشود. به مثال زیر توجه کنید:
1class SampleClass{
2 companion object{
3 fun create(sample :String): String{
4
5 return sample
6
7 }
8 }
9}
10fun SampleClass.Companion.printString(){
11 println("Companion Object Extensions")
12
13}
14fun main(args: Array<String>){
15 val sampleObject = SampleClass.printString("Print the string")
16 println(sampleObject)
17
18}
سخن پایانی
کاتلین از امکان بسط یک کلاس با کارکردهای جدید بدون پیادهسازی وراثت و یا بهرهگیری از یک الگوی طراحی پشتیبانی میکند. این کار از طریق یک علامت خاص انجام میشود که تابع اکستنشن نام دارد. این قابلیت برای تمیزتر و خواناتر کردن کد مفید است و حجم کد را نیز کاهش میدهد. با این حال این روش عواقب ناخوشایندی نیز دارد. برای نمونه، نمیتوانید به مشخصههای حفاظتشده یا تابعهای کلاس مبنا دسترسی داشته باشید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی اندروید (Android) – مقدماتی
- زبان برنامه نویسی کاتلین (Kotlin) — راهنمای کاربردی
- برنامه نویسی اندروید با کاتلین — راهنمای شروع به کار
==