الواجهات المغلقة مع السجلات
الواجهات المغلقة مع السجلات
في البرمجة الوظيفية يوجد مفهوم يُسمى النوع الجبري للبيانات (ADT) — وهو نوع يكون فيه الكائن واحدًا بالضبط من مجموعة معروفة ومحدودة من المتغيرات. فكّر في الأمر كعائلة مغلقة من الأشكال: Shape إمّا Circle أو Rectangle أو Triangle — ولا شيء آخر أبدًا. يمنحك Java 17 دعمًا أصيلًا لهذا النمط بالجمع بين الواجهات المغلقة (sealed interfaces) والسجلات (records).
لماذا يهمّ هذا الجمع
الواجهة المغلقة تقول: "فقط هذه الأنواع يمكنها تطبيقي." والسجل يقول: "أنا حامل بيانات شفّاف وغير قابل للتغيير." معًا يتيحان لك نمذجة متغير في النطاق كقيمة مُسمّاة وواصفة لنفسها دون أي نماذج تكرارية. يعرف المُصرِّف كل حالة ممكنة — وهو ما يجعل تعبيرات switch الشاملة (التي تُغطّى في الدرس التالي) آمنة على مستوى التصريف.
sealed class في Kotlin، أو data في Haskell، أو enum بحقول في Rust.
نمذجة نتيجة الدفع
تخيّل نظام دفع حيث تنتهي معالجة شحنة بإحدى ثلاث نتائج: نجاح، أو رفض ناعم (قابل لإعادة المحاولة)، أو فشل صارم. إليك النوع الجبري:
لاحظ ما حصلنا عليه مجانًا من تصريحات السجل: منشئات، equals، hashCode، toString، وأدوات الوصول — كلها مبنية على المكوّنات المُعلَن عنها. الواجهة المغلقة لا تضيف سوى القيد: هذه الأنواع الثلاثة فقط هي الموجودة.
جملة permits مقابل الاستنتاج التلقائي
إذا أُعلِن عن جميع الأنواع المسموح بها في وحدة تصريف واحدة (نفس ملف .java أو نفس الحزمة حسب التداخل)، يمكنك حذف جملة permits ويستنتجها المُصرِّف تلقائيًا. لكن التصريح الصريح يكون عادةً أفضل للقابلية على القراءة في كود الإنتاج:
permits الصريح لأي نوع جبري قد ينمو أو يعيش عبر ملفات متعددة. الاستنتاج الضمني مريح للعائلات الصغيرة المتجاورة — كما في اختبار أو حزمة أداة صغيرة.
السجلات يمكنها تطبيق واجهات متعددة
يمكن للسجل تطبيق أكثر من واجهة، بما فيها خليط من الواجهات المغلقة والعادية. وهذا مفيد عندما يحتاج متغيّر ما أيضًا للمشاركة في تجريد آخر:
إضافة منشئات مدمجة للتحقق
تدعم السجلات داخل تسلسل هرمي مغلق المنشئات المدمجة للتحقق من الصحة. إذا كانت نتيجة Declined يجب أن تحمل دائمًا سببًا غير فارغ، طبّق ذلك عند الإنشاء:
يعمل المنشئ المدمج قبل تعيين الحقول — وهو المكان المناسب تمامًا لفحوصات الحارس.
التداخل: واجهات مغلقة داخل السجلات
يمكن أن تذهب الأنواع الجبرية إلى أعماق أكثر. لنفترض أن Failed تحتاج إلى التمييز بين خطأ شبكة وحجب احتيال. يمكنك تضمين واجهة مغلقة أخرى بداخلها:
الآن Failed هو نفسه سجل شفاف، لكن حقله kind هو نوع جبري داخلي مُصنَّف وقابل للمطابقة الشاملة.
استهلاك النوع الجبري (معاينة لمطابقة الأنماط)
حتى قبل switch الشامل لمطابقة الأنماط (الدرس 9)، يمكنك بالفعل استخدام أنماط instanceof لاستهلاك تسلسل السجلات المغلقة بشكل نظيف:
سطر throw new AssertionError هو حارس دفاعي. في الدرس 9 ستستبدل هذه الطريقة بأكملها بتعبير switch واحد، وسيتحقق المُصرِّف من معالجة كل متغيّر — دون الحاجة إلى حارس.
الخلاصة
يمنح الجمع بين الواجهات المغلقة والسجلات Java طريقة موجزة وآمنة على مستوى الأنواع لنمذجة أنواع المتغيرات المغلقة (الأنواع الجبرية). الواجهة المغلقة تُسمّي مجموعة المتغيرات وتُقيّدها؛ وكل سجل يوفّر بيانات المتغيّر بشفافية. يتتبّع المُصرِّف كل حالة ممكنة، مما يُتيح التحليل الشامل ويُلغي الحاجة إلى فروع احتياطية دفاعية. هذا النمط هو الأساس لـ switch بمطابقة الأنماط الذي ستكتبه في الدرس التالي.