🏗️ ما هو الـ Constructor؟
Constructor هو دالة خاصة داخل الكلاس تُنفذ تلقائيًا عند إنشاء كائن جديد (Object) من الكلاس، وتُستخدم بشكل أساسي لـ:
الفائدة | الشرح |
---|---|
✅ تهيئة الخصائص | إعطاء قيم ابتدائية للخصائص عند إنشاء الكائن. |
✅ تنفيذ تعليمات مبدئية | يمكن تنفيذ أوامر بمجرد إنشاء الكائن. |
✅ أولًا: Default Constructor (الباني الافتراضي)
🔹 التعريف:
هو الباني الذي يُنشأ تلقائيًا من قِبل Dart إذا لم تُنشئ أي باني بنفسك.
✨ مثال:
class Person {
String name = 'غير معروف';
int age = 0;
void greet() {
print('مرحبًا، اسمي $name وعمري $age');
}
}
void main() {
var p = Person(); // هنا Dart تستخدم الباني الافتراضي تلقائيًا
p.greet(); // مرحبًا، اسمي غير معروف وعمري 0
}
Dart✅ الملاحظات:
- لا تكتب constructor بنفسك.
- Dart تنشئه تلقائيًا.
- يسمح بإنشاء الكائن بدون تمرير أي بيانات.
- بمجرد ما تكتب أي Constructor بنفسك، Dart تحذف الـ default constructor تلقائيًا.
✅ ثانيًا: Parameterized Constructor (الباني المُعَمَّم أو المُخصص)
🔹 التعريف:
هو constructor تكتبه بنفسك ويأخذ باراميترات (parameters) لتهيئة الخصائص عند إنشاء الكائن.
🛠️ الطريقة الأولى: باستخدام this
class Person {
String name;
int age;
// Constructor
Person(this.name, this.age);
}
void main() {
var p = Person('أحمد', 25);
print(p.name); // أحمد
print(p.age); // 25
}
Dart🔍 شرح:
this.name
تعني أن المتغير الذي داخل الكلاس يُساوي القيمة القادمة من البراميتر.- Dart توفر طريقة مختصرة بدلاً من:
Person(String name, int age) {
this.name = name;
this.age = age;
}
Dart🛠️ الطريقة الثانية: Constructor مُسمي (Named Constructor)
يُستخدم عندما نريد إنشاء أكثر من constructor في نفس الكلاس.
class Person {
String name;
int age;
// Constructor عادي
Person(this.name, this.age);
// Constructor مسمى
Person.fromName(this.name) {
age = 0;
}
}
void main() {
var p1 = Person('ليلى', 22);
var p2 = Person.fromName('خالد');
print(p2.name); // خالد
print(p2.age); // 0
}
Dart🛠️ الطريقة الثالثة: استخدام Initializer List (قائمة التهيئة)
إذا أردت إعداد الخصائص قبل تنفيذ جسم الـ constructor:
class Point {
int x;
int y;
Point(int a, int b) : x = a, y = b {
print('تم إنشاء النقطة');
}
}
void main() {
var p = Point(3, 4);
print(p.x); // 3
print(p.y); // 4
}
Dart✅ ملخص سريع للفروق:
النوع | يأخذ قيم؟ | تكتبه أنت؟ | الفائدة |
---|---|---|---|
Default | ❌ لا | ❌ لا | إنشاء كائن بدون قيم. |
Parameterized | ✅ نعم | ✅ نعم | إدخال قيم مباشرة عند الإنشاء. |
Named | ✅ نعم | ✅ نعم | إنشاء كائن بأكثر من طريقة. |
Initializer List | ✅ نعم | ✅ نعم | تهيئة الخصائص قبل تنفيذ الجسم. |
✅ فوائد استخدام Parameterized Constructor:
الفائدة | الشرح |
---|---|
🎯 الدقة | يجبرك على إدخال البيانات المطلوبة عند إنشاء الكائن. |
🔒 الأمان | يمنع الكائن من أن يُنشأ دون بيانات ضرورية. |
🔁 التخصيص | يمكنك إنشاء أكثر من باني لتناسب حالات مختلفة. |
⛏️ تقليل الأخطاء | يمنع نسيان تهيئة الخصائص. |
🧼 الكود أنظف | بدل كتابة خصائص وقيم بعد إنشاء الكائن، تكون جاهزة من البداية. |
✅ مثال تطبيقي عملي:
class Student {
String name;
int grade;
// Constructor
Student(this.name, this.grade);
void showInfo() {
print('الطالب: $name - الدرجة: $grade');
}
}
void main() {
var s1 = Student('علي', 95);
var s2 = Student('منى', 88);
s1.showInfo(); // الطالب: علي - الدرجة: 95
s2.showInfo(); // الطالب: منى - الدرجة: 88
}
Dart✅ ملاحظة هامة:
إذا كتبت Constructor خاص، Dart لا تنشئ Constructor افتراضي لك.
وبالتالي، إذا أردت استخدام الاثنين، يجب أن تكتب كلاهما بنفسك.
✅ ما هو Named Constructor؟
Named Constructor هو نوع من الـ constructors نُسميه باسم مخصص داخل الكلاس، ويُستخدم عندما:
- نريد أكثر من طريقة مختلفة لإنشاء الكائن.
- نحتاج تهيئة بعض الخصائص دون غيرها.
- نرغب بمرونة أكبر من constructor واحد فقط.
✅ تركيب الـ Named Constructor:
class ClassName {
ClassName.namedConstructorName() {
// كود التهيئة
}
}
Dart✅ مثال بسيط:
class Car {
String brand;
int year;
// Constructor الأساسي
Car(this.brand, this.year);
// Named Constructor
Car.fromOldModel(String brandName) {
brand = brandName;
year = 2000;
}
void info() {
print('السيارة: $brand - سنة الصنع: $year');
}
}
void main() {
var car1 = Car('Toyota', 2023);
var car2 = Car.fromOldModel('Nissan');
car1.info(); // السيارة: Toyota - سنة الصنع: 2023
car2.info(); // السيارة: Nissan - سنة الصنع: 2000
}
Dart✅ لماذا نستخدم Named Constructors؟
السبب | الشرح |
---|---|
🔁 تعدد الطرق | لإنشاء الكائن بأكثر من طريقة حسب الحالة. |
🔒 وضوح النية | الاسم يوضح الغرض (مثلاً: fromJson، fromDatabase، fromName). |
🛠️ تسهيل التحويلات | يمكنك تحويل البيانات من مصادر مختلفة لكائنات. |
🧱 تنظيم الكود | يجعل الكود أكثر ترتيبًا ومرونة. |
✅ مثال عملي آخر: تحويل JSON إلى كائن
class User {
String name;
int age;
// Constructor عادي
User(this.name, this.age);
// Named Constructor لتحويل JSON إلى كائن
User.fromJson(Map<String, dynamic> json)
: name = json['name'] ?? 'غير معروف',
age = (json['age'] ?? 0).toInt();
// دالة لعرض المعلومات
void show() {
print('👤 الاسم: $name - العمر: $age');
}
}
void main() {
// بيانات JSON على شكل List من الخرائط
List<Map<String, dynamic>> usersData = [
{'name': 'ياسين', 'age': 30},
{'name': 'سارة', 'age': 25},
{'name': 'أحمد'}, // العمر ناقص
{'age': 40}, // الاسم ناقص
{}, // الكل ناقص
];
// تحويل القائمة إلى قائمة من كائنات User
List<User> users = usersData.map((json) => User.fromJson(json)).toList();
// عرض كل المستخدمين
for (var user in users) {
user.show();
}
}
Dart✅ هل يمكن استخدام أكثر من Named Constructor في نفس الكلاس؟
نعم بكل تأكيد ✅
🔸 مثال:
class Shape {
double width;
double height;
// Constructor أساسي
Shape(this.width, this.height);
// مربع
Shape.square(double size) {
width = size;
height = size;
}
// مستطيل
Shape.rectangle(this.width, this.height);
void info() {
print('العرض: $width - الارتفاع: $height');
}
}
void main() {
var s1 = Shape.square(10);
var s2 = Shape.rectangle(10, 20);
s1.info(); // العرض: 10 - الارتفاع: 10
s2.info(); // العرض: 10 - الارتفاع: 20
}
Dart✅ مقارنة بين Constructors المختلفة:
النوع | الاسم | يستخدم this؟ | يأخذ قيم؟ | التخصيص |
---|---|---|---|---|
العادي | ClassName(...) | ✅ غالبًا | ✅ نعم | محدود |
Named | ClassName.name(...) | ✅ غالبًا | ✅ نعم | مرن وأكثر قابلية للتخصيص |
✅ ملاحظات مهمة:
- يمكن استخدام أكثر من Named Constructor في نفس الكلاس.
- لا يوجد Overloading في Dart مثل بعض اللغات الأخرى، لذلك الـ Named Constructors هو الحل الوحيد لعدة طرق للتهيئة.
- يمكن استدعاء الـ Named Constructor بنفس طريقة أي Constructor
var obj = ClassName.namedConstructorName(...);
Dart✅ تمرين لك (اختياري):
اكتب كلاس اسمه Student
، واجعل فيه:
- Constructor عادي.
- Named Constructor اسمه
fromMap
يستقبلMap
يحتوي علىname
وgrade
. - دالة
info()
تطبع بيانات الطالب.
✅ كود التمرين الكامل:
class Student {
String name;
double grade;
// Constructor العادي
Student(this.name, this.grade);
// Named Constructor: fromMap
Student.fromMap(Map<String, dynamic> data) {
name = data['name'];
grade = data['grade'].toDouble(); // تأكدنا أنها double
}
// دالة لطباعة المعلومات
void info() {
print('اسم الطالب: $name');
print('الدرجة: $grade');
}
}
void main() {
// استخدام الـ Constructor العادي
Student s1 = Student('أحمد', 92.5);
s1.info();
print('---');
// استخدام الـ Named Constructor fromMap
Map<String, dynamic> studentData = {
'name': 'سارة',
'grade': 87
};
Student s2 = Student.fromMap(studentData);
s2.info();
}
Dart✅ ماذا يفعل الكود؟
السطر | ما يفعله |
---|---|
Student(this.name, this.grade); | Constructor عادي يُمرر القيم مباشرة. |
Student.fromMap(...) | Constructor مسمى يأخذ Map (مثل من JSON أو قاعدة بيانات) ويحوله إلى كائن. |
s1.info() و s2.info() | تطبع بيانات الطالب في كل حالة. |
✅ المخرجات:
اسم الطالب: أحمد
الدرجة: 92.5
---
اسم الطالب: سارة
الدرجة: 87.0
Dart✅ الفوائد التعليمية:
- تعلمت الفرق بين constructor العادي والمسمى.
- كيفية استقبال Map وتحويله إلى كائن.
- تنظيم الكود وتقسيم طرق إنشاء الكائنات بحسب الحاجة.