11

Enum، Sealed و Generics؛ نظم برای حالت‌های پیچیده

وقتی داده‌ها چند حالت مشخص دارن، نذار مثل گله گوسفند هرکدوم یه طرف برن.

درس 11 از 20

Enum برای حالت‌های ثابت

اگر چند مقدار ثابت داری، `enum class` بساز. مثلاً وضعیت سفارش: جدید، پرداخت‌شده، ارسال‌شده. با رشته خام ننویس که یه جا `Paid` بزنی یه جا `Payed` و بعد سه روز دنبال باگ بگردی.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

enum
enum class OrderStatus {
    NEW, PAID, SHIPPED, CANCELED
}

fun main() {
    val status = OrderStatus.PAID
    println(status)
}
خروجی یا نتیجه اجرا
PAID
نکته کاربردی: این بخش اسکلت ذهنی درس را می‌سازد. وقتی بدانی این قطعه دقیقاً چه مسئله‌ای را حل می‌کند، بعداً موقع پروژه فقط syntax حفظی تحویل نمی‌دهی؛ تصمیم درست می‌گیری. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

Sealed class برای نتیجه‌های کنترل‌شده

`sealed class` وقتی خوبه که چند حالت محدود اما داده‌دار داری؛ مثلاً Loading، Success، Error. عالی برای UI state و پاسخ API.

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

sealed class
sealed class UiState {
    object Loading : UiState()
    data class Success(val data: String) : UiState()
    data class Error(val message: String) : UiState()
}

fun render(state: UiState) = when (state) {
    UiState.Loading -> "در حال بارگذاری..."
    is UiState.Success -> "داده: ${state.data}"
    is UiState.Error -> "خطا: ${state.message}"
}
خروجی یا نتیجه اجرا
اگر render(UiState.Success("OK")) را صدا بزنی:
داده: OK
نکته کاربردی: اینجا باید به رفتار کد نگاه کنی، نه فقط ظاهرش. مقدارها را عوض کن و دوباره اجرا بگیر تا دستت بفهمد برنامه چطور واکنش نشان می‌دهد. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

Generics؛ جعبه برای هر نوع

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

مثال را با چند مقدار دیگر هم امتحان کن؛ چون برنامه‌نویسی با نگاه کردن یاد گرفته نمی‌شود، با خراب کردن و درست کردن جا می‌افتد.

generic function
fun <T> printBox(value: T) {
    println("[$value]")
}

fun main() {
    printBox(123)
    printBox("Kotlin")
}
خروجی یا نتیجه اجرا
[123]
[Kotlin]
نکته کاربردی: این الگو در پروژه واقعی زیاد تکرار می‌شود. از همین حالا تمیز و خوانا بنویس تا بعداً موقع دیباگ، خودت به خودت فحش ندهی. اگر خروجی با چیزی که انتظار داشتی فرق کرد، اول مقدار متغیرها و مسیر اجرا را چک کن؛ ۹۰٪ باگ‌های اول راه همین‌جا قایم شده‌اند.

نتیجه این درس 🎯

Enum، sealed و generics سه ابزار برای کنترل حالت‌های پیچیده‌اند. با این‌ها کدت هم امن‌تر می‌شود هم whenها و مدل‌های نتیجه‌ات قابل اعتمادتر.

تمرین! 🧠

یک sealed class برای ApiResult بساز که Success و Failure داشته باشد و با when پیام مناسب چاپ کن.

جواب تمرین
sealed class ApiResult {
    data class Success(val body: String) : ApiResult()
    data class Failure(val error: String) : ApiResult()
}

fun message(result: ApiResult): String = when (result) {
    is ApiResult.Success -> "موفق: ${result.body}"
    is ApiResult.Failure -> "ناموفق: ${result.error}"
}

fun main() {
    println(message(ApiResult.Success("OK")))
}
خروجی جواب
موفق: OK