کلاس Sealed در کاتلین — به زبان ساده

۴۵۹ بازدید
آخرین به‌روزرسانی: ۰۱ مهر ۱۴۰۲
زمان مطالعه: ۴ دقیقه
کلاس Sealed در کاتلین — به زبان ساده

آیا اگر می‌توانستیم یک نوع منفرد را که می‌تواند در شکل‌های مختلفی ظاهر می‌شود، به طرزی نمایش دهیم که هر یک بتوانند ثابت باشند و یا داده‌های خود را ذخیره سازند مفید نبود؟ اگر مشغول برنامه‌نویسی با کاتلین هستید این شانس را دارید که دقیقاً همین کارکرد را با استفاده از کلاس Sealed در کاتلین به دست آورید.

997696

انواع شمارشی (Enum) سال‌ها است که در زبان‌های مختلف برنامه‌نویسی حضور دارند و به ما امکان می‌دهند که یک نوع را که مقدار آن از مجموعه محدودی از مقادیر اخذ می‌شود را بازنمایی کنیم. کاتلین این مفهوم را گرفته و آن را به چیزی بسیار قدرتمندتر تکامل داده است که به نام کلاس «نفوذناپذیر» (Sealed) شناخته می‌شود. این کلاس‌ها یکی از جنبه‌های بسیار مفید این زبان محسوب می‌شوند و برخی کاربردهای قدرتمندی دارند که به ما امکان می‌دهند برخی API-های بسیار زیبا بسازیم. در ادامه به شیوه استفاده از کلاس‌های نفوذناپذیر می‌پردازیم و مزیت‌های آن‌ها و برخی کاربردهای نمونه آن‌ها را مورد بررسی قرار می‌دهیم.

چگونه از کلاس نفوذناپذیر استفاده کنیم؟

هم Enum-ها و هم کلاس‌های نفوذناپذیر یک نوع را نشان می‌دهند که می‌تواند یک مقدار را از مجموعه‌ای از مقادیر بالقوه اخذ کند. یک Enum شامل مجموعه‌ای از مقادیر ثابت است و هر وهله از آن به یکی از این ثابت‌ها انتساب می‌یابد. اما کلاس‌های Sealed بر مبنای مجموعه‌ای از زیرکلاس‌ها کار می‌کنند و به ما امکان می‌دهند که وهله‌های متعددی از هر زیرکلاس داشته باشیم که هر یک حالت (State) خاص خود را دارند.

زیرکلاس مربوط به کلاس Sealed می‌تواند حالت خاص خود را داشته باشد.

فرض کنید یک AuthenticationState داریم که برای ردگیری وضعیت حساب کاربر درون اپلیکیشن مورد استفاده قرار می‌گیرد. کاربر می‌تواند با یک شناسه کاربری وارد شود و با برخی اطلاعات احراز هویت که ذخیره شده است خارج شود.

1sealed class AuthenticationState {
2  data class SignedIn(val userGuid: UUID) : AuthenticationState()
3  data class StoredCredentials(val credentials: Credentials) : AuthenticationState()
4  object SignedOut : AuthenticationState()
5}

با استفاده از یک کلاس نفوذناپذیر، یک مشخصه از نوع AuthenticationState باید یکی از زیرکلاس‌های انتساب یافته به خود را داشته باشد. می‌توانیم از یک کلاس داده استفاده کنیم تا مشخصه‌های خاصی به هر زیرکلاس بدهیم یا این که یک شیء به آن انتساب بدهیم و آن را به صورت ثابت دربیاوریم.

برخلاف Enum، این زیرکلاس نیازی ندارد که بدنه کلاس نفوذناپذیر را نگهداری کند و تنها درون همان فایل قرار می‌گیرد.

1sealed class AuthenticationState
2data class SignedIn(val userGuid: UUID) : AuthenticationState()
3data class StoredCredentials(val credentials: Credentials) : AuthenticationState()
4object SignedOut : AuthenticationState()

روشی که برای انتخاب ساختار کلاس‌های نفوذناپذیر خود انتخاب می‌کنیم، بر شیوه ارجاع دادن به آن‌ها تأثیر دارد. مثال فوق موجب تولید یک AuthenticationState.SignedOut یا SignedOut می‌شود. سپس می‌توانیم زیرکلاس را درون کلاس نفوذناپذیر قرار دهیم تا یک فضای نام برای نوع خود ایجاد کنیم.

کلیدواژه When

یکی از موقعیت‌هایی که کلاس‌های نفوذناپذیر کاملاً برجسته هستند، درون عبارت when است. استفاده از when به عنوان یک عبارت موجب می‌شود که مقدار انتسابی یا بازگشتی از سوی کامپایلر برای تعیین این که آیا همه حالت‌های بالقوه مدیریت شده است یا نه مورد استفاده قرار گیرد و دیگر نیازی به شاخه else نداریم.

1fun onAuthenticationStateChanged(newState: AuthenticationState) = when (newState) {
2  is AuthenticationState.SignedIn -> showSignedIn(newState.userGuid)
3  is AuthenticationState.StoredCredentials -> showSignedIn(newState.credentials)
4  AuthenticationState.SignedOut -> showSignedOut()
5}

کامپایلر می‌تواند تشخیص دهد که آیا همه حالت‌ها مدیریت شده‌اند یا نه.

با استفاده از کلاس‌های نفوذناپذیر درون API می‌توانیم مدیریت همه حالت‌های ممکن را برای مصرف‌کنندگان واقعاً آسان کنیم. زمانی که از آن درون کد خود استفاده کنیم، اگر یک زیرکلاس دیگر اضافه کنیم، هر گزاره when که به صورت یک عبارت استفاده شود باید آن را مدیریت کند.

حالت نما

در اپلیکیشن‌های اندرویدی (یا اپلیکیشن‌های GUI) باید روشی برای اتصال منطقی که UI را کنترل می‌کند به خود نماها پیدا کنیم. یک روش این فرایند می‌تواند شامل مدل‌سازی حالت نما باشد. این حالت می‌تواند شامل بارگذاری، نمایش یک خطا یا نمایش دادن داده‌ها باشد.

1sealed class ViewState
2object LoadingState : ViewState()
3data class PresentingState(val viewData: ContactsViewData) : ViewState()
4data class ErrorState(val message: String) : ViewState()

تصور کنید مشغول توسعه یک صفحه هستید که فهرستی از مخاطبان را نمایش می‌دهد که از یک پایگاه داده لوکال بارگذاری شده‌اند و لایه نمای ما ViewState را دریافت می‌کند و می‌تواند نماهای درست را بر اساس این که کدام زیرکلاس از کلاس نفوذناپذیر دریافت شده است رندر کند.

1fun renderViewState(viewState: ViewState) = when (viewState) {
2  LoadingState -> showLoadingViews()
3  is PresentingState -> showPresentingViews(viewState.viewData)
4  is ErrorState -> showErrorViews(viewState.message)
5}

استفاده از یک کلاس نفوذناپذیر به ما امکان می‌دهد که نماهای خود را برای همه حالت‌های ممکن نما به‌روزرسانی کنیم و همراه با هر حالت مجموعه داده‌های متفاوتی ذخیره‌سازیم:

  • LoadingState می‌تواند صرفاً یک object بدون داده باشد.
  • PresentingState به همراه خود ContactsViewData را برای رندر کردن مخاطبان می‌آورد.
  • ContactsViewData شامل پیام خطایی است که نمایش می‌یابد.

بدون استفاده از یک کلاس نفوذناپذیر، باید از کلاسی استفاده می‌کردیم که شامل همه داده‌ها به صورت مشخصه‌های تهی‌پذیر (nullable) باشد و همچنین یک enum می‌داشتیم که نشان دهد در کدام حالت قرار دارد.

رویدادهای تحلیلی

در اغلب موارد لازم است که تشخیص دهیم چند نفر از اپلیکیشن‌های ما استفاده می‌کنند و یا اثربخشی اقدامات خاصی را از طریق استفاده از ابزارهای تحلیلی (analytics) بسنجیم. این حالت به طور معمول منجر به ایجاد رویدادهایی می‌شود که پس از اقدامات خاص درون اپلیکیشن اجرا می‌شوند. این رویدادهای تحلیلی به همان ترتیب گزارش می‌شوند، اما ممکن است مشخصه‌های متفاوتی به آن‌ها الصاق شده باشند. این یکی از کاربردهای مناسب کلاس‌های نفوذناپذیر محسوب می‌شود. کدی که باید رویداد را مدیریت کند، می‌تواند این کار را به سادگی با استفاده از عبارت when انجام دهد. برای نمونه می‌توان هر رویداد را به map مشخصه‌های آن نگاشت کرد.

1sealed class AnalyticsEvent {
2    object AccountCreated : AnalyticsEvent()
3    data class MessageSent(val conversation: Conversation) : AnalyticsEvent()
4    data class ProfileOpened(val participant: Participant) : AnalyticsEvent()
5
6    fun parameters(): Map<Parameter, String> = when (this) {
7        AccountCreated -> mapOf()
8        is MessageSent -> mapOf(
9            Parameter.PARTICIPANT_COUNT to conversation.participants.size.toString(),
10            Parameter.IS_ARCHIVED to conversation.isArchived.toString()
11        )
12        is ProfileOpened -> mapOf(
13            Parameter.HAS_ACCOUNT to participant.hasCreatedAccount.toString()
14        )
15    }
16
17    enum class Parameter {
18        HAS_ACCOUNT,
19        IS_ARCHIVED,
20        PARTICIPANT_COUNT,
21    }
22}

به جای آن می‌توانیم یک رویداد تحلیلی را به صورت یک کلاس داده ساده با یک نام و map مشخصه‌ها مدل‌سازی کنیم. یکی از مشکلاتی که این رویکرد دارد، این است که می‌تواند نام‌های رویدادها، نام‌های مشخصه‌ها و ایجاد رویدادها را روی همه کدبیس گسترش دهد. مزیت رویکرد کلاس نفوذناپذیر این است که می‌توانیم تعریف همه رویدادها را در یک محل نگهداری کنیم و مطمئن باشیم که صحیح هستند و مشخصه‌های صحیحی را گزارش می‌کنند. این که کلاس‌های نفوذناپذیر به ما امکان بدهند که انعطاف‌پذیری یک نوع AnalyticsEvent را داشته باشیم و بتوانیم علی‌رغم این که شکل‌های متفاوتی دارند آن‌ها را به هر کجا ارسال کنیم، بسیار عالی است.

سخن پایانی

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

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۳ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
proandroiddev
۲ دیدگاه برای «کلاس Sealed در کاتلین — به زبان ساده»

اول خودتون یاد بگیرید بعد به بقیه اموزش بدید
یا یه مترجم خوب استخدام کنید

سلام و وقت بخیر دوست عزیز؛
همه تلاش من به عنوان مترجم این متن در این جهت بوده که مفاهیم پیچیده و غامض را در حد امکان به زبانی ساده و گویا به مخاطبان محترم منتقل کنم.
اگر در این راه توفیق زیادی نداشته‌ام، لطفاً عذرخواهی مرا بپذیرید و با ارائه راهنمایی بیشتر به غنی‌تر شدن دانش من کمک بفرمایید.
با سپاس فراوان.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *