Kotlin'de Yüksek Seviye Fonksiyonlar (High Order Functions)

Merhaba

Bu yazıda Kotlin'de yüksek seviye fonksiyonların kullanımını anlatacağım. "Kotlin fonksiyonel dil özelliklerine sahiptir" cümlesini pek çok Kotlin ve Android hayranından duymuş olabilirsiniz (Ben dahil). Nedir yani bu fonksiyonel dil özellikleri hadi örnek ver hadi… diyorsanız, toplanın yamacıma.

Fonksiyonel dillerde fonksiyonlar özeldir tek başlarına bireylerdir. Literatürde bu first class tabiri ile ifade edilir. Yani String, Integer veya bir Sınıf ile fonksiyonlar aynı seviyededirler. Fonksiyonlara parametre olarak String veya Integer yollayabildiğimiz gibi first class fonksiyonlara sahip dillerde parametre olarak fonksiyon da yollayabiliriz. Fonksiyona parametre olarak fonksiyon yolluyoruz yani. Aynı şekilde başka fonksiyonlardan return tipi olarak fonksiyon da döndürebiliriz. Fonksiyonlar Java'nın aksine fonksiyonel dillerde herhangi bir sınıf içerisinde yer almak zorunda değillerdir. Fonksiyon çağıran fonksiyonlara yüksek seviye fonksiyonlar ismi verilir ve fonksiyonel diller yüksek seviye fonksiyon özelliğine sahiptirler. Kotlin de doğal olarak bu özelliğe sahiptir. Ayrıca fonksiyonel dillerde fonksiyonlar veri yapılarında ve değişkenlerde tutulabilirler. Özet olarak High Order Functions, Kotlin'in gözde özelliklerinden birisidir. Şimdi önce küçük bir örnek ile daha iyi açıklayacağım. Ardından Android'de real-life kullanımını göstererek pekiştireceğim. İzninizle, Başlayalım...

 

Parametre olarak fonksiyon kabul eden bir fonksiyonun tanımlanması aşağıdaki gibi yapılır.

funpassMeFunction ( text:String , function : ( ) -> Unit ) {
       // do something
       //execute the function
       function()
}

Bu fonksiyon, şu 5 şekilde çağırılabilir:
 

passMeFunction ( "text" ) { print ( it ) }

passMeFunction ( "text" ) { s -> print ( s ) }

passMeFunction ( "text" , { print ( it ) } )

passMeFunction ( "text" , { s -> print ( s ) } )

passMeFunction("text", ::print )  // En pratik versiyon tabiiki bu. İt veya herhangi bir başka değişkene gerek kalmaksızın Fonksiyon Referansı kullanarak kodu daha da basitleştirebiliriz.
KISS : Keep It Small Stupid hell yeeaa

 

Ben açıkçası bu özelliğe ihtiyaç duyduğumun farkında bile değilmişim. Kullanmaya başladığımda farkettim ki pek çok yerde high order function kullanabilirim.

Özellikle orta ve daha büyük ölçekte bir proje yazarken pek çok defa aynı koda başka Activitylerde Fragmentlarda Sınıflarda ihtiyaç duyarsınız. Bu normaldir. Mesela kullanıcı silme işlemi kullanıcı listesinden de gerçekleşebilir kullanıcı detay sayfasından da. Ancak kopyala yapıştır yapmaya başlarsanız büyük projede ilerleyen safhalarda boğulabilirsiniz. Çöp kod üretebilirsiniz ve kod yönetimini kendi elinizle zorlaştırabilirsiniz. Her projeyi başından düzgün yazmanızı öneririm ki ilerleyen zamanda refactor için çok daha fazla zaman harcamanız gerekmesin.

Real-life örneğe gelirsek. Peakup'ta çalışanların hızlıca izin alabilmelerini, izin sürecini takip edebilmelerini, takım liderlerinin ve insan kaynaklarının da izin süreçlerini kolayca yönetebilmelerini sağlayan bir Android uygulamamız var. Leave Management. Bu uygulamamızı yazarken bir sorunla karşılaştım. İzin silme, izin onaylama ve izin reddetme işlemleri için hemen hemen aynı fonksiyonu yazdığımı ve gereksiz kod tekrarı yaptığımı farkettim. 3 operasyonda da İzin detay DialogFragment'ını önce kapatıp sonra silmek / reddetmek / onaylamak istediğinize emin misiniz? diye soran bir dialog çıkartıyordum. Kullanıcı onay butonuna basınca da ilgili fonksiyon aracılığı ile backende request yolluyordum. Yazılan fazladan kodu farkettiğimde High Order Function özelliği geldi aklıma. Çünkü 3 fonksiyonda da sadece Dialog'da yazan text ve çağırılan request fonksiyonu farklıydı. Ve şöyle bir Fonksiyon yazdım:
 

private fun showAlertAndOperate(@StringRes stringId: Int, leave: Leave, func: () -> Unit) {
        leaveDetailsDialogFragment.dismiss()       // İzin detay dialog fragmentının kapatılması

        val text = getString(stringId, leave.User.getNameSurname(),  LeaveDate.getDayAndTimeByFormat(leave.CreatedAt, LeaveDate.prettyFormatLongDateTime), leave.Reason.Name)
        showAlertDialog(text, DialogInterface.OnClickListener {_, which ->
               if (which == DialogInterface.BUTTON_POSITIVE) {
                       func()
                       /* func() fonksiyonu methoda gönderilen fonksiyon.
                          Dialog onay butonuna basıldığında, showAlertAndOperate fonksiyonuna gönderilen fonksiyon her ne ise burada çağırılıyor. */
                }
        })
}

 

showAlertAndOperate ismini verdiğim bu fonksiyon, 3 adet parametre alıyor. İlk parametresi bir Int ancak annotation olarak StringRese sahip. Yani bu fonksiyona göndereceğiniz ilk parametre R.string.xxx gibi bir string id olacak. İkinci parametre bir leave objesi. Son parametre ise işlemi yapacak olan fonksiyon. Son parametrede eğer izin silme işlemi yapacaksak izin silme requestini yapan fonksiyon, izin onaylama işlemi yapacaksak onaylama requestini yollayan fonksiyon çağırılacak yani.

showAlertAndOperate fonksiyonu önce izin detay dialog fragmentını kapatıyor. Ardından normal Dialog içerisinde gösterilecek olan texti StringRes ve Leave parametrelerini kullanarak hazırlıyor. Ardından bu texti showAlertDialog fonksiyonuna yollayarak Global alertdialogun çıkmasını sağlıyor. Dialogun onay butonuna basıldığı takdirde ise fonksiyona parametre olarak gönderilen fonksiyon çalışıyor.

 

override fun onLeaveApprove(leave: Leave)  =
        showAlertAndOperate(R.string.are_you_sure_to_accept_leave, leave) { confirmLeave(leave, MODE_ACCEPTED) }

override fun onLeaveDecline(leave: Leave)  =
        showAlertAndOperate(R.string.are_you_sure_to_decline_leave, leave) { confirmLeave(leave, MODE_DECLINED) }

override fun onLeaveDelete(leave: Leave) =
        showAlertAndOperate(R.string.are_you_sure_to_remove_leave, leave) { deleteLeave(leave) }

 

Yukarıda gördüğünüz fonksiyonlar ise İzin detay dialog fragmentına ait interface içerisinde bulunan buton onClick işleminde çalışan callbackler. Gördüğünüz üzre onLeaveApprove'da confirmLeave fonksiyonu accepted modunda, onLeaveDecline'da confirmLeave fonksiyonu declined modunda çağırılmış. onDelete'te ise deleteLeave() fonksiyonu çağırılmış. showAlertAndOperate fonksiyonunda belirtilen func() fonksiyonu yerine bu fonksiyonlar çağırılacak.

Kodu kopyala yapıştır yapmamak için tabiiki pek çok farklı yöntem de mevcut. Bu yazıda bu yöntemlerden sadece birinden bahsedebildim. En güzel yöntem budur golden keydir gibi iddialara sahip değilim. Sadece bu seferlik bunu tercih ettim ve sizlerle paylaşmak istedim. Yazının resimli ve daha geniş bir versiyonuna buradan ulaşabilirsiniz

Bol kodlu günler dilerim.

Blog yazılarına abone olmak ister misiniz ?

Her yeni blog yazısı çıktığında veya soru yayınlandığında bildirim almak için e-mail adresinizi yazmanız yeterli.