Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الواجهات الضمنية

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 46 من 110.

بما أن كل Dart class تحدد interface تلقائياً، يمكنك استخدام أي class كـ interface - حتى الـ concrete classes ذات التنفيذات الكاملة. هذا ما نسميه implicit interface.

تأمل فئة (class) تحتوي على خصائص (properties) ودوّال (methods):

class Logger {
  String prefix = '[LOG]';
  
  void log(String message) {
    print('$prefix $message');
  }
  
  void clear() {
    print('Clearing logs...');
  }
}

عندما تقوم فئة (class) أخرى بتنفيذ Logger، يجب عليها تقديم تنفيذها الخاص لـ كل شيء - بما في ذلك خاصية prefix:

class FileLogger implements Logger {
  @override
  String prefix = '[FILE]';
  
  @override
  void log(String message) {
    print('$prefix Writing to file: $message');
  }
  
  @override
  void clear() {
    print('Deleting log file...');
  }
}

هذه هي الفكرة الجوهرية: عندما تستخدم implements، فأنت تقول "سيكون لفئتي (class) نفس الشكل" - نفس الأساليب (methods) والخصائص (properties). أنت لا ترث أي كود، أو دوال بناء (constructors)، أو قيم افتراضية. أنت تَعِد فقط بمطابقة هيكل الواجهة (interface).

هذا يجعل الواجهات الضمنية قوية لإنشاء مكونات قابلة للتبديل. يمكن للكود الذي يتوقع Logger أن يعمل مع أي فئة تنفذ واجهته، بغض النظر عن كيفية عمل هذا التنفيذ داخلياً.

challenge icon

التحدي

سهل

لنقم ببناء نظام إشعارات يوضح كيف يمكن للفئات الملموسة (concrete classes) أن تعمل كواجهات ضمنية (implicit interfaces). ستقوم بإنشاء فئة إشعارات أساسية ثم تنفيذها بطريقة مختلفة تماماً، مما يوضح أن implements تتطلب منك توفير جميع الأعضاء بنفسك.

ستقوم بتنظيم الكود الخاص بك في ملفين:

  • notification.dart: حدد فئات الإشعارات الخاصة بك هنا:
    • فئة Notification مع خاصية String channel مضبوطة على 'default'، وطريقة send(String message) تطبع [channel]: [message]، وطريقة getStatus() تعيد السلسلة النصية 'Notification ready'
    • فئة EmailNotification التي تنقذ (implements) Notification. بما أنك تستخدم implements، يجب عليك توفير خاصية channel الخاصة بك (اضبطها على 'email')، وطريقة send() الخاصة بك التي تطبع Sending email: [message]، وطريقة getStatus() الخاصة بك التي تعيد 'Email service active'
    • فئة PushNotification التي تنقذ (implements) Notification. اضبط الـ channel الخاص بها على 'push'، واجعل send() تطبع Push alert: [message]، واجعل getStatus() تعيد 'Push service connected'
  • main.dart: استورد ملف الإشعارات الخاص بك ووضح كيف يمكن للفئات المختلفة تنفيذ نفس الواجهة الضمنية بسلوكيات مختلفة تماماً:
    • أنشئ كائناً من EmailNotification وآخر من PushNotification
    • لكل إشعار، اطبع حالته باستخدام getStatus()، ثم استدعِ send() مع الرسالة 'Hello World'
    • اطبع سطراً فارغاً بين الإشعارين

تذكر: عندما تستخدم implements، فأنت لا ترث أي كود من Notification. أنت فقط تتعهد بأن فئتك سيكون لها نفس الشكل - نفس الخصائص والطرق. توفر كل فئة منفذة تنفيذها الكامل الخاص بها.

المخرجات المتوقعة:

Email service active
Sending email: Hello World

Push service connected
Push alert: Hello World

ورقة مرجعية

في لغة Dart، يحدد كل كلاس تلقائياً واجهة ضمنية (implicit interface). وهذا يعني أنه يمكنك استخدام أي كلاس كواجهة، حتى الكلاسات الملموسة (concrete classes) ذات التنفيذ الكامل.

عندما يستخدم كلاس الكلمة المفتاحية implements، يجب عليه توفير تنفيذه الخاص لـ جميع الخصائص والأساليب (methods) من الواجهة. أنت لا ترث أي كود أو منشئات (constructors) أو قيم افتراضية - بل تعد فقط بمطابقة هيكل الواجهة.

مثال على كلاس ملموس (concrete class):

class Logger {
  String prefix = '[LOG]';
  
  void log(String message) {
    print('$prefix $message');
  }
  
  void clear() {
    print('Clearing logs...');
  }
}

تنفيذ الواجهة الضمنية:

class FileLogger implements Logger {
  @override
  String prefix = '[FILE]';
  
  @override
  void log(String message) {
    print('$prefix Writing to file: $message');
  }
  
  @override
  void clear() {
    print('Deleting log file...');
  }
}

هذا يجعل الواجهات الضمنية قوية لإنشاء مكونات قابلة للتبديل. الكود الذي يتوقع Logger يمكنه العمل مع أي كلاس ينفذ واجهته، بغض النظر عن التنفيذ الداخلي.

جرّب بنفسك

import 'notification.dart';

void main() {
  // TODO: EmailNotification قم بإنشاء مثيل من
  
  // TODO: getStatus() قم بطباعة حالة إشعار البريد الإلكتروني باستخدام
  
  // TODO: باستخدام إشعار البريد الإلكتروني 'Hello World' قم بإرسال رسالة
  
  // TODO: قم بطباعة سطر فارغ
  
  // TODO: PushNotification قم بإنشاء مثيل من
  
  // TODO: getStatus() قم بطباعة حالة إشعار الدفع باستخدام
  
  // TODO: باستخدام إشعار الدفع 'Hello World' قم بإرسال رسالة
  
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming