Sealed Class — ما هو، مبدأ العمل والتطبيق

المؤلف: IT Sectr نُشر: 2026-05-26 وقت القراءة: 8 دق

Sealed Class هو نوع خاص من الفئات في Kotlin يحدد تسلسل الوراثة بمجموعة ثابتة من الأنواع الفرعية. جميع الأنواع الفرعية تُعلن في نفس الملف ويكون المترجم على علم بها، مما يسمح باستخدام كتلة when شاملة بدون فرع else إلزامي. وفقًا Kotlin Docs, 2026، الفئات المختومة هي آلية رئيسية لتمثيل التسلسلات الهرمية المحدودة مثل الحالات وأنواع الأخطاء وأحداث واجهة المستخدم.

أهم النقاط

  • Sealed Class — فئة بمجموعة ثابتة من الأنواع الفرعية المعلنة في نفس الملف.
  • Exhaustive when — يتحقق المترجم من معالجة جميع الأنواع الفرعية، مما يستبعد فروع else المنسية.
  • Sealed interface — Kotlin 1.5+ يدعم الواجهات المختومة للوراثة المتعددة.
  • التسلسل الهرمي للأخطاء — sealed class هو الطريقة القياسية لمعالجة الأخطاء بشكل آمن من حيث النوع في Kotlin.
  • الفرق عن enum — يمكن لكل نوع فرعي من sealed class أن يحتوي على حالة فريدة وعدد مختلف من الحقول.

ما هو Sealed Class؟

Sealed Class (فئة مختومة) هي فئة في Kotlin موسومة بالمُعدل sealed. وهي تحدد تسلسلاً هرمياً محدوداً للأنواع: جميع الأنواع الفرعية المحتملة مدرجة في نفس الملف والمترجم يعرف كل منها. وهذا يمييز sealed class عن الفئة المفتوحة العادية التي يمكن تعريف أنواعها الفرعية في أي مكان.

الهدف الرئيسي من sealed class هو تمثيل آمن من حيث النوع لمجموعة محدودة من الخيارات. يمكن لكل نوع فرعي أن يكون له هيكل بيانات خاص به، مما يجعل sealed class أكثر مرونة من enum. في وقت التشغيل، sealed class هي فئة مجردة عادية؛ يفرض المترجم قيودًا فقط في وقت الترجمة.

Sealed class مفيد بشكل خاص في هندسة تطبيقات Android: حالات واجهة المستخدم، نتائج طلبات الشبكة، أحداث التنقل الشبيهة بـ Intents، وبالطبع التسلسلات الهرمية للأخطاء — كلها حالات استخدام نموذجية.

في وقت الترجمة، يتم تحسين sealed class إلى جدول قفز لتعبيرات when، مما يجعله أكثر كفاءة من سلاسل if-else. وعند دمجه مع data class، يمكن لكل نوع فرعي أن يحتوي ليس فقط على حالة ولكن أيضًا على طرق، مما يسمح ببناء نماذج مجال موثقة ذاتيًا بدون كود شائع.

Sealed class فعال أيضًا في تمثيل آلات الحالة في التطبيقات المحمولة. كل حالة هي نوع فرعي منفصل بمعلمات فريدة، ويتم التحكم في التحولات بين الحالات عبر تعبير when. يضمن المترجم معالجة جميع الحالات الممكنة، مما يستبعد أخطاء وقت التشغيل عند تغيير حالة واجهة المستخدم أو منطق الأعمال.

Sealed Class و Enum: الفروق الرئيسية

غالبًا ما يخلط مطورو Kotlin المبتدئون بين sealed class و enum، حيث أن كلاهما يحد من مجموعة القيم. ومع ذلك، هناك فرق جوهري: enum هو مجموعة من الثوابت من نفس النوع، بينما sealed class هو تسلسل هرمي لأنواع مختلفة.

متى تختار enum

Enum هو الأمثل عندما تكون جميع الخيارات ثوابت بدون بنية إضافية. على سبيل المثال، أيام الأسبوع، حالات الطلب، أو أنواع الإجراءات بدون معلمات. كل قيمة enum هي singleton باسم ثابت.

متى تختار sealed class

Sealed class مطلوب عندما يكون لكل خيار بياناته الخاصة. على سبيل المثال، خطأ الشبكة يحتوي على رمز الاستجابة، خطأ التحليل يحتوي على تفاصيل، وخطأ التفويض يحتوي على رسالة. كل نوع فرعي من sealed class هو نوع منفصل بحقول فريدة.

kotlin
// Enum — جميع الخيارات من نوع واحد
enum class Status { LOADING, SUCCESS, ERROR }

// Sealed class — كل خيار ببياناته الخاصة
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String) : UiState<Nothing>()
}

Sealed Interface مقابل Sealed Class

منذ Kotlin 1.5، أصبح من الممكن تعريف sealed interface. وهذا يوسع مفهوم الختم للواجهات: sealed interface لديها أيضًا مجموعة ثابتة من التطبيقات ولكنها تدعم الوراثة المتعددة.

متى تستخدم sealed interface

Sealed interface مفيد عندما تحتاج الأنواع الفرعية إلى تطبيق عدة عقود في وقت واحد. على سبيل المثال، يمكن أن يكون حدث واجهة المستخدم قابلًا للنقر والتتبع في نفس الوقت. مع sealed class، سيتعين عليك اختيار فئة أساسية واحدة؛ مع sealed interface، يقوم النوع الفرعي بتطبيق كليهما.

قيود sealed class

Sealed class هي فئة، لذلك يمكن لكل نوع فرعي أن يكون له أب واحد فقط. Sealed interface يحل هذه المشكلة لكنه لا يمكنه احتواء حالة. يعتمد الاختيار بينهما على المهمة: إذا كانت هناك حاجة لمنطق مشترك مع حقول — استخدم sealed class؛ إذا كانت هناك حاجة لمرونة العقود — استخدم sealed interface.

kotlin
sealed interface ScreenEvent {
    data class Refresh(val force: Boolean) : ScreenEvent
    data class Navigate(val route: String) : ScreenEvent
    data class ShowError(val toast: String) : ScreenEvent
}

sealed interface AnalyticsEvent {
    val name: String
    val params: Map<String, Any>
}

// النوع الفرعي يطبق كلتا الواجهتين
data class LoginClicked(
    override val name: String = "login_click",
    override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent

Sealed Class للتسلسل الهرمي للأخطاء في تطوير التطبيقات المحمولة

أحد الاستخدامات الرئيسية لـ sealed class في تطوير التطبيقات المحمولة هو التسلسلات الهرمية الآمنة من حيث النوع للأخطاء. بدلاً من رمي استثناءات من أنواع مختلفة أو استخدام Exception عام، يجمع sealed class جميع أخطاء المجال الممكنة في نوع واحد.

كيفية بناء تسلسل هرمي للأخطاء

أنشئ sealed class باسم DomainError واذكر جميع أنواع الفشل كأنواع فرعية. يحتوي كل نوع فرعي فقط على البيانات ذات الصلة بهذا النوع المعين من الخطأ. يضمن المترجم أنك عند معالجة الخطأ لن تنسى أي خيار.

مثال: معالجة أخطاء المصادقة

لنفكر في تطبيق مع تفويض حيث توجد سيناريوهات فشل مختلفة: كلمة مرور خاطئة، حساب محظور، مشكلة في الخادم. يجمعها sealed class في نوع واحد بمعالجة شاملة.

kotlin
sealed class AuthError {
    data class InvalidCredentials(
        val attempts: Int
    ) : AuthError()

    data class AccountBlocked(
        val until: Long
    ) : AuthError()

    data class NetworkFailure(
        val cause: Throwable
    ) : AuthError()

    object ServerError : AuthError()
}

fun handleError(error: AuthError): String = when (error) {
    is AuthError.InvalidCredentials ->
        "المحاولات المتبقية: ${3 - error.attempts}"
    is AuthError.AccountBlocked ->
        "الوصول محظور حتى ${Date(error.until)}"
    is AuthError.NetworkFailure ->
        "تحقق من الاتصال: ${error.cause.localizedMessage}"
    AuthError.ServerError ->
        "الخادم غير متاح مؤقتًا"
}

أنماط استخدام Sealed Class في Android

Sealed class أصبح أداة قياسية في هندسة تطبيقات Android. دعونا نلقي نظرة على ثلاثة أنماط رئيسية حيث يكون sealed class لا غنى عنه في تطوير التطبيقات المحمولة.

  • UI State — تمثيل الشاشة كآلة حالة: Loading, Content, Error. كل حالة تحتوي على بياناتها الخاصة، ويضمن sealed class معالجة جميع التحولات.
  • Navigation Event — sealed class بدلاً من ثوابت التنقل: كل شاشة هي نوع فرعي منفصل مع معلمات المسار. المترجم يتحقق من أنواع الوسائط.
  • Action/Intent — نمط Unidirectional Data Flow يستخدم sealed class لتمثيل جميع الإجراءات التي يمكن للمستخدم تنفيذها على الشاشة.

كما تجدر الإشارة إلى استخدام sealed class في Clean Architecture. كل طبقة (data, domain, presentation) تستخدم sealed class لأنواع الأخطاء الخاصة بها، وتقوم المحولات (mappers) بتحويل sealed class إلى آخر. على سبيل المثال، DataError من طبقة البيانات يتم تحويله إلى DomainError لمنطق الأعمال، ثم إلى UiState لطبقة العرض. هذا يحافظ على أمان الأنواع في جميع مستويات التطبيق ويضمن عدم بقاء أي خطأ دون معالجة.

اختبار التسلسلات الهرمية لـ Sealed Class

اختبار sealed class يتطلب نهجًا خاصًا، حيث أن كل نوع فرعي هو نوع منفصل بحالته الخاصة. يُوصى بكتابة اختبارات معلمة تمر عبر جميع الأنواع الفرعية لـ sealed class. وهذا يضمن أن تعبيرات when تغطي جميع الخيارات، بما في ذلك الجديدة المضافة عند توسيع التسلسل الهرمي.

لـ اختبارات واجهة المستخدم، sealed class كـ UiState يسمح بالتحقق من عرض كل حالة: Loading يُظهر spinner، Content يُظهر البيانات، Error يُظهر رسالة الخطأ. بما أن sealed class محدود، فإن التغطية الاختبارية لجميع الحالات تعطي ثقة كاملة في صحة منطق واجهة المستخدم.

الأخطاء الشائعة عند العمل مع Sealed Class

على الرغم من بساطة المفهوم، فإن المطورين يرتكبون أخطاء بانتظام عند تصميم التسلسلات الهرمية لـ sealed class. دعونا نلقي نظرة على المشاكل الرئيسية وكيفية تجنبها.

  • أنواع فرعية في ملفات مختلفة — لن يسمح المترجم بتعريف sealed class إذا كانت الأنواع الفرعية خارج الملف. هذا القيد يضمن when شامل.
  • خلط sealed و open — لا يمكن لـ sealed class أن تكون open في نفس الوقت. إذا كانت هناك حاجة لتسلسل هرمي قابل للتوسيع، استخدم abstract class عادي، لكنك ستضحي بالشمولية.
  • التداخل المفرط — sealed class داخل sealed class يُنشئ تسلسلاً هرمياً عميقاً يصعب صيانته. للسيناريوهات البسيطة، مستويان كافيان.
  • نسيان else في when — إذا كانت sealed class من مكتبة بدون شمولية، لن يحذرك المترجم عن فرع مفقود. أضف else فقط عن قصد.

الأسئلة الشائعة

هل يمكن أن تحتوي sealed class على طرق مجردة؟

نعم، يمكن لـ sealed class أن تحتوي على طرق مجردة، وكل نوع فرعي ملزم بتطبيقها. هذا مفيد عندما يجب أن توفر جميع الخيارات واجهة مشتركة ولكن بمنطق تنفيذ مختلف.

هل sealed class متاحة في Java؟

في Java 17+، تم تقديم الفئات والواجهات المختومة بالمُعدل sealed. Android يدعم حالياً Java 17 جزئياً، لكن في مشاريع Kotlin، sealed class متاح منذ Kotlin 1.0 بدون قيود.

هل يمكن لـ sealed class أن ترث من sealed class أخرى؟

نعم، يمكن أن تكون sealed class نوعاً فرعياً من أخرى. يبقى التسلسل الهرمي لـ sealed classes محدوداً: المترجم يعرف جميع الأنواع الفرعية في كل مستوى. هذا يسمح ببناء تصنيفات مفصلة للأخطاء.

هل تؤثر sealed class على الأداء؟

Sealed class لا تخلق عبئاً إضافياً في وقت التشغيل. يقوم المترجم بتحسين تعبيرات when مع sealed classes إلى جداول قفز (tableswitch)، وهي أسرع من سلاسل if-else. الأداء مطابق لـ enum.

كيفية اختبار التسلسلات الهرمية لـ sealed class؟

يتم اختبار كل نوع فرعي من sealed class بشكل منفصل. بما أن sealed class محدود، يمكن كتابة اختبار معلمة يمر عبر جميع الخيارات. وهذا يعطي تغطية كاملة لفروع كتلة when.

الملخص

  • Sealed class — فئة بمجموعة ثابتة من الأنواع الفرعية المعلنة في ملف واحد، مما يتيح تحليلاً شاملاً لـ when في وقت الترجمة.
  • يمكن لكل نوع فرعي من sealed class أن يكون له هيكل بيانات خاص به — هذا هو الفرق الرئيسي عن enum، حيث جميع الخيارات ثوابت من نفس النوع.
  • Sealed interface (Kotlin 1.5+) يدعم الوراثة المتعددة، sealed class يدعم الوراثة الفردية فقط. يعتمد الاختيار على الحاجة إلى حالة مشتركة.
  • Sealed class هو الآلية القياسية للتسلسلات الهرمية الآمنة من حيث النوع للأخطاء في Kotlin: كل نوع فشل هو نوع فرعي منفصل بحقول ذات صلة.
  • الأنماط الرئيسية في Android: UI State و Navigation Event و Action/Intent — تُبنى على sealed class لضمان اكتمال المعالجة.
  • تجنب الأنواع الفرعية في ملفات مختلفة والتداخل المفرط وخلط sealed مع open — فهذا ينتهك عقد التسلسل الهرمي المحدود.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا