الكلاسات في Dart: البرمجة كائنية التوجه
في Dart كل شيء كائن (Object): الأرقام والنصوص والقوائم، وحتى كل Widget في Flutter. والكائنات تُصنع من كلاسات (Classes). في هذا الدرس ستتعلم كيف تصمم كلاساتك الخاصة، وستفهم لماذا يُعد هذا الدرس المفتاح الحقيقي لفهم Flutter.
الكلاس والكائن
Section titled “الكلاس والكائن”الكلاس مثل القالب أو المخطط الهندسي، والكائن هو الشيء الحقيقي المصنوع منه. مخطط سيارة واحد يُصنع منه آلاف السيارات، لكل منها لون ورقم لوحة مختلف.
class Student { String name; int age;
Student(this.name, this.age);
void introduce() { print('أنا $name وعمري $age سنة'); }}
void main() { var s1 = Student('سارة', 14); var s2 = Student('عمر', 15); s1.introduce(); s2.introduce();
s2.age = 16; print(s2.age);}أنا سارة وعمري 14 سنةأنا عمر وعمري 15 سنة16- الخصائص (Properties):
nameوage، وهي بيانات كل كائن. - الدوال (Methods):
introduce، وهي ما يستطيع الكائن فعله. - المُنشئ (Constructor):
Student(this.name, this.age)، الدالة التي تصنع الكائن. والصيغةthis.nameاختصار ذكي في Dart يعني «ضع القيمة الأولى في الخاصية name مباشرة». - اسم الكلاس بحرف كبير
Student، وكل كائن مستقل: تغيير عمر عمر لم يؤثر على سارة.
المُنشئ بالمعاملات المسماة
Section titled “المُنشئ بالمعاملات المسماة”تماماً كما تعلمت في درس الدوال، يمكن أن يستخدم المُنشئ معاملات مسماة، وهذا الأسلوب المعتاد في Flutter:
class Product { final String name; final double price; final int quantity;
Product({required this.name, required this.price, this.quantity = 1});
double get total => price * quantity;}
void main() { var p = Product(name: 'كتاب', price: 60, quantity: 3); print(p.total);}180.0لاحظ أن الخصائص final: بعد إنشاء المنتج لا تتغير بياناته. والكائنات التي لا تتغير أأمن وأسهل في التتبع، و Flutter يعتمد عليها كثيراً.
الـ getter
Section titled “الـ getter”double get total => price * quantity; تبدو كدالة، لكنها تُقرأ كخاصية بدون أقواس: p.total وليس p.total(). استخدمها للقيم المحسوبة من خصائص أخرى.
المُنشئ المسمى
Section titled “المُنشئ المسمى”يمكن أن يكون للكلاس أكثر من طريقة لإنشائه، كل واحدة باسم:
class Product { // ... نفس الخصائص والمُنشئ السابق
Product.free(this.name) : price = 0, quantity = 1;}
void main() { print(Product.free('ملصق').total);}0.0Product.free طريقة مختصرة لإنشاء منتج مجاني. والجزء بعد النقطتين : يسمى قائمة التهيئة (Initializer List)، ويعطي الخصائص قيمها قبل اكتمال الكائن.
الخصائص الخاصة
Section titled “الخصائص الخاصة”في Dart، أي اسم يبدأ بشرطة سفلية _ يكون خاصاً (Private)، لا يُرى من خارج الملف:
class BankAccount { final String owner; double _balance = 0;
BankAccount(this.owner);
double get balance => _balance;
void deposit(double amount) { if (amount <= 0) { print('المبلغ يجب أن يكون أكبر من صفر'); return; } _balance += amount; }}
void main() { var acc = BankAccount('منى'); acc.deposit(500); acc.deposit(-20); print(acc.balance);}المبلغ يجب أن يكون أكبر من صفر500.0الرصيد _balance محمي: لا يستطيع أحد من خارج الكلاس كتابة acc._balance = 1000000. الطريقة الوحيدة لتغييره هي deposit التي تتحقق من المبلغ أولاً. أما القراءة فمتاحة عبر الـ getter balance. هذه الفكرة اسمها التغليف (Encapsulation)، وهي مثل ماكينة الصراف: ترى رصيدك وتودع فيه، لكن لا تستطيع فتح الخزنة بيدك.
الوراثة: extends
Section titled “الوراثة: extends”كلاس يمكن أن يرث كل خصائص ودوال كلاس آخر، ثم يضيف أو يعدّل عليها:
class Animal { final String name; Animal(this.name);
void speak() { print('$name يصدر صوتاً'); }}
class Cat extends Animal { Cat(super.name);
@override void speak() { print('$name يقول: مياو'); }}
void main() { var animals = [Animal('الحصان'), Cat('مشمش')]; for (var a in animals) { a.speak(); }}الحصان يصدر صوتاًمشمش يقول: مياوextends Animal: القطة نوع من الحيوانات، فترثnameوspeak.super.name: مرّر الاسم لمُنشئ الأب.@override: نعيد كتابة دالة موروثة بسلوك خاص.
toString
Section titled “toString”عند طباعة كائن، تطبع Dart افتراضياً شيئاً مثل Instance of 'Product'. أعد كتابة toString لتطبع شيئاً مفيداً:
@overrideString toString() => 'Product($name, $price)';الآن print(p) تطبع Product(كتاب, 60.0).
جرّب بنفسك
Section titled “جرّب بنفسك”صمم كلاس Book فيه: العنوان والمؤلف وعدد الصفحات (معاملات مسماة، والمؤلف إجباري)، و getter اسمه isLong يُرجع true إذا تجاوزت الصفحات 300، ودالة describe تطبع وصف الكتاب. ثم أنشئ كتابين واستدعِ describe لكل منهما.
تحدٍّ: أنشئ EBook extends Book يضيف خاصية حجم الملف، ويعيد كتابة describe.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان this في المُنشئ المختصر:
Student(name, age);لا يعطي الخصائص قيماً. الصحيحStudent(this.name, this.age);. - خصائص بدون قيم: خاصية
String name;بلا مُنشئ يملؤها، يرفضها المحرر بسبب Null Safety. - نسيان @override: الكود يعمل، لكن المحرر ينبهك، وكتابتها توضح أنك تعيد كتابة دالة موروثة عن قصد.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ما معنى أن يبدأ اسم الخاصية بشرطة سفلية مثل _balance؟
الشرطة السفلية في بداية الاسم تجعله خاصاً (Private)، وهذا يحمي البيانات من التعديل العشوائي.
سؤال
كيف تقرأ getter اسمه total من كائن اسمه p؟
الـ getter يُقرأ كخاصية عادية بدون أقواس، رغم أنه يُحسب كدالة.
الخلاصة
Section titled “الخلاصة”- الكلاس قالب، والكائن نسخة حقيقية منه لها بياناتها الخاصة.
this.nameفي المُنشئ اختصار للإسناد، والمعاملات المسماة معrequiredأسلوب Flutter.getللقيم المحسوبة، والمُنشئ المسمى لطرق إنشاء إضافية._تجعل الأسماء خاصة لحماية البيانات.extendsللوراثة و@overrideلإعادة الكتابة، وهذا هو أساس Widgets في Flutter.