Kotlin ile Az İf Çok İş

Herkese selamlar bu yazımda Kotlin ile objeler üzerinde validasyon işlemleri üzerinde biraz konuşup temiz kodlar yazarak İf-Else kullanımını azaltacağız.


Kas 01, 2021
hamurcuabi

1
481

Öncelikle neden İf-Else kullanımını azaltarak kod yazmamız gerektiğine değinelim. Kodun okunabilirliği ve geliştirmeye açık olması dışında CPU üzerinde yüklü bir maliyet çıkardığını unutmayalım. Şuraya bu konuyla ilgili yararlı bi link bırakıyorum.

Ekranımızda birden çok dolduralabilir alan olduğunu düşünelim ve bunları kullanıcıdan isteyelim. Tabiki her alan için özel isteklerimiz var.

data class User(
        var name: String = "", // No empty, min 5 character
        var surname: String = "", //Not empty
        var email: String = "", // Not empty, Email format
        var age: Int = 0, // min age 18
    )

Kötü Yöntem

 private fun validateOldWay() {
        val pair = if (user.name.isNullOrEmpty()) {
            Pair(false, "Username cannot be empty")
        } else if (user.name.length < 5) {
            Pair(false, "Username lenght min 5 character")
        } else if (user.email.isNullOrEmpty()) {
            Pair(false, "Email cannot be empty")
        } else if (user.email.isValidEmail().not()) {
            Pair(false, "Email format is wrong")
        } else if (user.age < 18) {
            Pair(false, "Age min 18")
        } else Pair(true, "VALID")

        if (pair.first) {
            // VALID DO SOMETHING
        } else {
            //NOT VALID !!
        }
    }
 

Yukardaki kodu when kullanarak da yazabilirdik ama bu sorunumuzu aslında çözmüyor. Backend tarafından şöyle bir istek geldiğini düşünelim User data classımıza Tc numarasınıda artık almamız gerekiyor denirse ne yapacağız? Ya da artık age 18 değilde 16 ya düştü ve daha bi sürü if-else düzenlesi, değiştirmesi yapılacak. Muhtemelen bir yerden sonra bu kod blokları çok büyüyecek ve okunması inanılmaz zor hale gelecek. Peki bu durumu nasıl çözebiliriz?

Yeni Yaklaşım

.Net ile backend kodlama yaptığım zamanlarda FluentValidation kütüphanesini çok sık kullanıyordum. Buradaki yapıya benzer bir yapı kurmaya çalıştım.

İlk olarak validasyon işlemimizin sonucunu alabileceğimiz bir sealed class yazarak başlayalım

sealed class ValidationState {
    object Valid : ValidationState()
    data class InValid(val errorMessage: String?) : ValidationState()
}

Şimdi işimizi yapacak classı yaratalım.


/**
 * Rule is our business logic
 * @param T is a generic type of any
 */
typealias Rule = (value: T?) -> Boolean

class Validator(private val data: T) {

private val validationRules = ArrayList&gt;()
private var errorMessages = ArrayList()

/**
 * @param errorMsg if condition is wrong should return this
 * @param rule business logic
 *
 * @sample addRule(&quot;Name Cannot be empty&quot;) { it?.name.isNullOrEmpty() }
 */
fun addRule(errorMsg: String, rule: Rule) {
    validationRules.add(rule)
    errorMessages.add(errorMsg)
}

/**
 * Returns ValidationState
 */
fun isValid(): ValidationState {
    for (i in 0 until validationRules.size) {
        if (validationRules[i](data)) {
            val errorMsg = errorMessages[i]
            return ValidationState.InValid(errorMsg)
        }
    }
    return ValidationState.Valid
}

/**
 * Returns a list of invalid ValidationState
 */
fun inValidList(): List {
    val invalidStates = ArrayList()

    for (i in 0 until validationRules.size) {
        if (validationRules[i](data)) {
            val errorMsg = errorMessages[i]
            invalidStates.add(ValidationState.InValid(errorMsg))
        }
    }
    return invalidStates
}

/**
 * Returns a list of invalid ValidationState
 */
fun validList(): List {
    val validStates = ArrayList()

    for (i in 0 until validationRules.size) {
        if (validationRules[i](data).not()) {
            validStates.add(ValidationState.Valid)
        }
    }
    return validStates
}

}

Şimdi yeni yaklaşımımızla tekrar kodlayalım. User nesnemizin her bir elemanı için addRule ile ayrı ayrı logic yazabiliriz.

 val user = User()
        val userValidator = Validator(user).apply {
            addRule("Name Cannot be empty") { it?.name.isNullOrEmpty() }
            addRule("Username lenght min 5 character") { it?.name!!.length < 5 }
            addRule("Email cannot be empty") { it?.email.isNullOrEmpty() }
            addRule("Email format is wrong") { it?.email.isValidEmail() }
            addRule("Age min 18") { it?.age!! < 18 }
        }

Validator’ümüzü yazdık hemen ardından ValidateResolver’ınıda kodlayalım.

val validateResolver = ValidatorResolver(listOf(userValidator))
        when (val state = validateResolver.isValid()) {
            is ValidationState.Valid -> "Valid Do Something"
            is ValidationState.InValid -> "Not Valid ${state.errorMessage}"
        }

Kodlarımızı yazdık if-else kullanımını neredeyse sıfırladık ve kodun okunabilirliğini arttırdık. Yeni bir özellik eklenmek istenirse ya da varolan bir logic değiştirmek istenirse bunu yapmanın ne kadar basit olduğunu gördük. Projenin github linkini ve gist kodlarını paylaşıyorum. Herkese temiz kodlamalı günler dilerim.

android kotlin

Benzer Yazılar


Yorumlar


Kasım 05, 202112:23 @sezginzgurr@gmail.com

Farklı bir bakış açısı. Eline sağlık