تكرار السؤال: …
الإجابة المختصرة
تعلن sealed تسلسلًا مغلقًا من الأنواع الفرعية داخل نفس المكتبة، فيتحقق المترجم من أن switch يغطي كل الحالات دون default؛ وتتحكم معدِّلات مثل final و base و interface في من يمكنه الوراثة أو التنفيذ.
الشرح
الفئة sealed مجردة ضمنيًا ولا يمكن وراثتها أو تنفيذها خارج مكتبتها، لذلك يعرف المترجم كل أنواعها الفرعية. عند إضافة نوع جديد يظهر خطأ ترجمة في كل switch لم يعالجه، وهذا يجعلها مثالية لنمذجة حالات الشاشة (Loading/Loaded/Failed) ونتائج العمليات. أما final فتمنع الوراثة والتنفيذ خارج المكتبة، و base تسمح بالوراثة وتمنع التنفيذ، و interface تسمح بالتنفيذ وتمنع الوراثة، و mixin class تجمع سلوك الـ class والـ mixin.
مثال بالكود
sealed class LoadState {}
class Loading extends LoadState {}
class Loaded extends LoadState {
Loaded(this.items);
final List<String> items;
}
class Failed extends LoadState {
Failed(this.message);
final String message;
}
Widget buildBody(LoadState state) => switch (state) {
Loading() => const CircularProgressIndicator(),
Loaded(:final items) => ListView(children: [for (final i in items) Text(i)]),
Failed(:final message) => Text(message),
}; // adding a new subclass turns this switch into a compile error until it is handled
final class Money { // cannot be extended or implemented outside this library
const Money(this.cents);
final int cents;
}
interface class Repository { // can be implemented, but not extended, outside the library
Future<void> save() async {}
}أخطاء شائعة
- إضافة default إلى switch على نوع sealed، فتختفي أخطاء الترجمة عند إضافة حالات جديدة وتضيع فائدة الفحص الشامل.
- الظن أن sealed تمنع الوراثة داخل نفس المكتبة؛ هي تقيّد الأنواع الفرعية بنفس المكتبة فقط.
أسئلة متابعة
- ما هي Records و Patterns في Dart 3 وكيف تغيّر طريقة إرجاع عدة قيم وتفكيك البيانات؟
- كيف تختار approach لإدارة state دون اعتبار package واحدة حلًا عالميًا؟