✅ أولاً: ما هو الـ Interface؟
📘 التعريف:
interface هو عقد (contract) يحدد ما الذي يجب على الكلاس تنفيذه، لكنه لا يحتوي على أي منطق (body).
في Dart، لا يوجد keyword خاصة بـ interface، وإنما كل class يمكن اعتباره interface عند استخدامه مع implements.
🧠 كيف نستخدمه؟
- Dart تعوض
interface بالكلمة المفتاحية: implements.
- الكلاس الذي يطبّق (implements) interface يجب أن ينفذ كل شيء فيه حرفيًا.
✅ لماذا نستخدم interface؟
| الهدف | الشرح |
|---|
| ✅ فصل السلوك عن التنفيذ | الكلاس يحدد فقط “ما يجب فعله” وليس “كيف” |
| ✅ قابلية التوسعة | أي كلاس يمكنه تنفيذ الواجهة بطريقته |
| ✅ دعم الوراثة المتعددة | Dart لا تدعم extends لأكثر من كلاس، لكن تدعم implements لعدة واجهات |
| ✅ يساعد في كتابة كود مرن وقابل للاختبار | لأنه يفصل بين الكود الفعلي والتجريد |
✅ الشكل العام:
class InterfaceName {
void method1();
int method2(String name);
}
class MyClass implements InterfaceName {
@override
void method1() {
// تنفيذ خاص بالكلاس
}
@override
int method2(String name) {
return name.length;
}
}
Dart
✅ مثال 1: واجهة تسجيل الدخول
class AuthService {
void login(String email, String password);
}
class FirebaseAuth implements AuthService {
@override
void login(String email, String password) {
print("تسجيل الدخول باستخدام Firebase");
}
}
class LocalAuth implements AuthService {
@override
void login(String email, String password) {
print("تسجيل الدخول المحلي");
}
}
void main() {
AuthService auth = FirebaseAuth();
auth.login("[email protected]", "123456");
}
Dart
✅ شرح المثال:
- أنشأنا
AuthService كـ واجهة: تحتوي فقط على الدالة login بدون body.
FirebaseAuth و LocalAuth قاموا بـ implements للواجهة ونفذوا دالة login بطريقتهم.
- الكود صار مرن: تقدر تستبدل طريقة تسجيل الدخول بسهولة دون تغيير منطق البرنامج.
✅ ملاحظات مهمة جدًا:
| المعلومة | التوضيح |
|---|
| ❌ لا يمكن ترك دالة بدون تنفيذ | يجب تنفيذ كل الدوال الموجودة في الواجهة |
| ✅ يمكن تنفيذ عدة واجهات معًا | Dart تدعم implements A, B, C... |
✅ الواجهة لا تحتوي خصائص final أو متغيرات منجزة | فقط التصريحات (declarations) |
| ✅ الواجهة لا تنفذ شيء | فقط تحدد ما يجب تنفيذه |
✅ مثال 2: حيوانات لها صوت
class SoundMaker {
void makeSound();
}
class Dog implements SoundMaker {
@override
void makeSound() {
print("هاو هاو 🐶");
}
}
class Cat implements SoundMaker {
@override
void makeSound() {
print("مياو 🐱");
}
}
Dart
✅ الفرق بين implements و extends و abstract class
| المقارنة | abstract class | interface (implements) | extends (وراثة عادية) |
|---|
| يحتوي منطق (دوال عادية)؟ | ✅ نعم | ❌ لا | ✅ نعم |
| يجب تنفيذ الدوال المجردة؟ | ✅ نعم | ✅ نعم | ❌ لا |
| يرث من كلاس واحد فقط؟ | ✅ نعم | ❌ يمكن من عدة واجهات | ✅ نعم |
| يمكن استخدام الخصائص؟ | ✅ نعم | ❌ فقط الدوال | ✅ نعم |
| وراثة كاملة للسلوك؟ | ❌ جزئي | ❌ لا | ✅ نعم |
✅ استخدام أكثر من واجهة:
class Printable {
void printInfo();
}
class Saveable {
void save();
}
class Document implements Printable, Saveable {
@override
void printInfo() => print("طباعة المستند");
@override
void save() => print("حفظ المستند");
}
Dart
وهكذا جعلنا Document يجمع بين وظيفتين مختلفتين.
✅ متى تستخدم interface؟ ومتى abstract class؟
| الحالة | استخدم interface | استخدم abstract class |
|---|
| عند الحاجة لتعريف هيكل بدون تنفيذ | ✅ | ❌ |
| عند الحاجة لمنطق مشترك بين الأبناء | ❌ | ✅ |
| عند الحاجة لتنفيذ أكثر من نوع | ✅ | ❌ |
| تريد فرض تنفيذ 100% للدوال | ✅ | ❌ |
| تريد مرونة كبيرة بالوراثة | ✅ (لأنها متعددة) | ❌ (واحدة فقط) |
✅ خلاصة شاملة:
| العنصر | التوضيح |
|---|
interface | مجرد عقد يجب على الكلاس تنفيذه |
implements | الطريقة التي نطبق بها interface |
| يجب تنفيذ كل شيء | أي دالة في الواجهة يجب تنفيذها بالكامل |
| مناسب لـ polymorphism والاختبار | لأنه يتيح لك تبديل التنفيذ بسهولة |
| لا يمكن إنشاء كائن من interface | لأنها ليست كلاس كامل التنفيذ |