تخطَّ إلى المحتوى

الـ Mixins والـ Enums في Dart

الدرس 10 من 14 · Dart

في هذا الدرس أداتان صغيرتان تجعلان كودك أنظف: Mixin لمشاركة قدرات بين كلاسات لا تجمعها وراثة، و Enum لتعريف قائمة ثابتة من الاختيارات مثل حالات الطلب أو أيام الأسبوع. كلتاهما مستخدمتان بكثرة في تطبيقات Flutter.

السمكة تسبح، والبطة تسبح وتطير، والعصفور يطير. أين نضع كود «السباحة» وكود «الطيران»؟ الكلاس يرث من أب واحد فقط، فلا تستطيع البطة أن ترث من «سابح» و«طائر» معاً. و implements لا تنفع أيضاً، لأنها لا تعطيك كوداً جاهزاً.

الحل هو Mixin: قطعة كود جاهزة تُضاف إلى أي كلاس. تخيّلها مثل إضافات البيتزا: العجينة واحدة، وتضيف عليها ما تشاء من زيتون وجبن وفطر.

mixin Swimmer {
String get name;
void swim() => print('$name يسبح');
}
mixin Flyer {
String get name;
void fly() => print('$name يطير');
}
class Animal {
final String name;
Animal(this.name);
}
class Fish extends Animal with Swimmer {
Fish(super.name);
}
class Duck extends Animal with Swimmer, Flyer {
Duck(super.name);
}
void main() {
var fish = Fish('السمك');
var duck = Duck('البط');
fish.swim();
duck.swim();
duck.fly();
print(duck is Flyer);
print(fish is Flyer);
}
الناتج
السمك يسبح
البط يسبح
البط يطير
true
false

لنفهم ما حدث:

  • نعرّف الإضافة بالكلمة mixin، ونضيفها للكلاس بالكلمة with.
  • Duck أخذت القدرتين معاً بفاصلة بينهما، دون تكرار أي كود.
  • السطر String get name; داخل الإضافة يقول: «أحتاج أن يكون في الكلاس الذي يستخدمني خاصية اسمها name». وقد وفّرها الأب Animal.
  • المعامل is يؤكد أن البطة تُعتبر Flyer فعلاً، فيمكن وضعها في قائمة من هذا النوع.

Enum: قائمة اختيارات ثابتة

Section titled “Enum: قائمة اختيارات ثابتة”

كيف تخزّن حالة طلب في متجر؟ لو استخدمت نصاً مثل 'shipped'، فأي خطأ إملائي مثل 'shiped' سيمر دون أن ينتبه أحد. Enum (اختصار enumeration، أي التعداد) يحل المشكلة: نوع جديد قيمه محددة مسبقاً، ولا يقبل غيرها.

enum OrderStatus { pending, shipped, delivered }
void main() {
var status = OrderStatus.shipped;
print(status);
print(status.name);
print(status.index);
print(OrderStatus.values.length);
var message = switch (status) {
OrderStatus.pending => 'طلبك قيد التجهيز',
OrderStatus.shipped => 'طلبك في الطريق',
OrderStatus.delivered => 'تم التسليم',
};
print(message);
}
الناتج
OrderStatus.shipped
shipped
1
3
طلبك في الطريق
  • name تعطي اسم القيمة نصاً، و index ترتيبها بدءاً من صفر.
  • OrderStatus.values قائمة بكل القيم، وهي مفيدة لعرض الاختيارات في قائمة منسدلة.
  • أهم ميزة تظهر مع switch: إذا نسيت حالة واحدة، سيرفض المحرر الكود. وإذا أضفت غداً حالة رابعة cancelled، فسيدلّك المحرر على كل مكان يحتاج تحديثاً.

قيم الـ enum يمكن أن تحمل بيانات. هذا enum لأحجام الأكواب في مقهى، ولكل حجم اسم عربي وسعر:

enum CupSize {
small('صغير', 15),
medium('وسط', 20),
large('كبير', 27);
final String label;
final int price;
const CupSize(this.label, this.price);
int priceFor(int count) => price * count;
}
void main() {
for (var size in CupSize.values) {
print('${size.label}: ${size.price} جنيه');
}
print(CupSize.large.priceFor(3));
print(CupSize.values.byName('medium').label);
}
الناتج
صغير: 15 جنيه
وسط: 20 جنيه
كبير: 27 جنيه
81
وسط

قواعد هذه الصيغة: القيم تُكتب أولاً وتنتهي بفاصلة منقوطة، والخصائص كلها final، والمُنشئ يبدأ بـ const. والتابع byName يحوّل نصاً إلى قيمة enum، وهو مفيد عند قراءة البيانات من JSON.

عرّف enum اسمه Level لمستويات لعبة: easy و normal و hard، ولكل مستوى عدد المحاولات المسموحة (10 و 7 و 4). ثم اطبع كل المستويات، واطبع بـ switch رسالة للمستوى الصعب:

الناتج
easy: 10 محاولات
normal: 7 محاولات
hard: 4 محاولات
حظاً موفقاً، ستحتاجه!
  • استخدام extends مع mixin: الإضافة تُستخدم بـ with وليس extends.
  • إنشاء كائن من mixin: Swimmer() مرفوضة. الإضافة ليست كلاساً كاملاً.
  • مقارنة enum بنص: status == 'shipped' دائماً false. قارن بالقيمة نفسها OrderStatus.shipped، أو استخدم status.name.
  • الاعتماد على index في التخزين: إذا حفظت الرقم ثم غيّرت ترتيب القيم لاحقاً، فستتغير معاني البيانات القديمة. احفظ name بدلاً منه.

سؤال

بأي كلمة نضيف mixin إلى كلاس؟

سؤال

في enum Color { red, green, blue }، ما قيمة Color.green.index؟

  • mixin قطعة كود جاهزة تُضاف إلى الكلاسات بالكلمة with.
  • الكلاس يرث من أب واحد، لكنه يأخذ أي عدد من الـ mixins.
  • enum نوع قيمه محددة مسبقاً، ويحميك من الأخطاء الإملائية.
  • switch مع enum يجبرك على تغطية كل الحالات.
  • قيم الـ enum يمكن أن تحمل خصائص وتوابع.