🏗️ ما هو الـ 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.0Dart✅ الفوائد التعليمية:
- تعلمت الفرق بين constructor العادي والمسمى.
- كيفية استقبال Map وتحويله إلى كائن.
- تنظيم الكود وتقسيم طرق إنشاء الكائنات بحسب الحاجة.
🔥 إضافات مفيدة على ملخص الـ Constructor
1️⃣ Constructor مع قيم اختيارية (Optional Parameters)
💡 مهم جدًا لأنه يُستخدم كثير في Dart
class Person {
String name;
int age; Person({this.name = 'غير معروف', this.age = 0});
}
✅ الفائدة:
- يعطي مرونة عند إنشاء الكائن
- ما يجبر المستخدم يمرر كل القيم
2️⃣ Constructor مع required
💡 يستخدم مع الـ named parameters
class Person {
String name;
int age; Person({required this.name, required this.age});
}
✅ الفائدة:
- يجبر المستخدم على إدخال القيم المهمة
- يزيد الأمان
3️⃣ Constant Constructor (const)
💡 نقطة متقدمة ومهمة
class Point {
final int x;
final int y; const Point(this.x, this.y);
}
✅ الفائدة:
- يستخدم لإنشاء كائنات ثابتة (immutable)
- يحسن الأداء
4️⃣ Factory Constructor
💡 من أهم الإضافات اللي ناقصة عندك
class Logger {
String name;
static final Map<String, Logger> _cache = {}; factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
} Logger._internal(this.name);
}
✅ الفائدة:
- ما ينشئ كائن جديد كل مرة
- ممكن يرجع كائن موجود
- مفيد في caching / singleton
5️⃣ Constructor خاص (Private Constructor)
💡 يستخدم مع التصميمات المتقدمة
class Singleton {
Singleton._internal(); static final Singleton instance = Singleton._internal();
}
✅ الفائدة:
- يمنع إنشاء كائن من خارج الكلاس
- يستخدم في Singleton pattern
6️⃣ ترتيب تنفيذ الـ Constructor
📌 معلومة نظرية مهمة:
- Initializer List تُنفذ أولًا
- ثم يتم تنفيذ جسم الـ constructor
✔️ مثال (موجود عندك جزئيًا):
Point(int a, int b) : x = a, y = b {
print('تم التنفيذ');
}
7️⃣ ملاحظة مهمة جدًا (تعزيز للفهم)
✔️ ذكرتها عندك لكن ممكن توضحها أكثر:
إذا كتبت Constructor واحد، Dart لن تنشئ Default Constructor
📌 يعني:
class A {
A(int x);
}
❌ لا يمكنك:
A(); // خطأ
🧠 إضافة صغيرة على الملخص النهائي
ممكن تضيف سطر ختامي قوي مثل:
👉 “الـ Constructor هو أول خطوة في حياة الكائن، وكلما كان تصميمه جيدًا، كان الكود أكثر أمانًا وتنظيمًا.”