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

دوال التوسعة (Extension Methods)

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

أحيانًا قد ترغب في إضافة وظائف إلى فئة (class) لا تملكها - مثل أنواع String أو int المضمنة في Dart. لا يمكنك تعديل الكود المصدري الخاص بها، كما أن الوراثة لا تساعد مع الفئات النهائية (final classes). هنا يأتي دور extension methods.

تسمح لك الامتدادات (Extensions) بإضافة دوال (methods) جديدة إلى الأنواع الموجودة دون تعديلها أو إنشاء فئات فرعية (subclasses):

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

void main() {
  print('hello'.reverse());        // olleh
  print('radar'.isPalindrome);     // true
}

الصيغة هي extension ExtensionName on Type. في الداخل، تقوم بتعريف الدوال (methods) والمسترجعات (getters) تماماً كما هو الحال في الكلاسات (classes) العادية. بمجرد تعريفها، تظهر هذه الدوال على جميع مثيلات (instances) ذلك النوع كما لو كانت مدمجة.

تعمل الامتدادات (Extensions) على أي نوع، بما في ذلك الفئات (classes) الخاصة بك والأنواع العامة (generic types):

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

void main() {
  print(5.squared());                    // 25
  print([1, 2, 3].secondOrNull);         // 2
}

لاحظ أنه داخل الامتداد (extension)، يشير this إلى المثيل (instance) الذي يتم استدعاء الطريقة عليه. تعد الامتدادات طريقة نظيفة لتحسين الأنواع الموجودة مع الحفاظ على تنظيم الكود الخاص بك وسهولة قراءته.

challenge icon

التحدي

سهل

دعنا نعزز أنواع Dart المدمجة بوظائف مفيدة باستخدام طرق التوسعة (extension methods)! ستقوم بإنشاء توسعات تضيف طرقاً مفيدة إلى النوعين String و List، ثم تستخدمها في ملفك الرئيسي.

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

  • extensions.dart: أنشئ توسعتين تضيفان إمكانيات جديدة للأنواع الموجودة:
    • توسعة تسمى StringUtils على String تحتوي على:
      • طريقة countVowels() تعيد عدد الحروف المتحركة (a, e, i, o, u - غير حساسة لحالة الأحرف) في السلسلة النصية
      • مسترجع (getter) يسمى initials يعيد الحرف الأول من كل كلمة (مقسمة بمسافات)، مدمجة معاً وبأحرف كبيرة. على سبيل المثال، "hello world" تصبح "HW"
    • توسعة تسمى ListUtils<T> على List<T> تحتوي على:
      • طريقة safeGet(int index) تعيد العنصر الموجود في الفهرس (index) إذا كان موجوداً، أو null إذا كان الفهرس خارج الحدود
      • مسترجع (getter) يسمى hasMultiple يعيد true إذا كانت القائمة تحتوي على أكثر من عنصر واحد
  • main.dart: استورد التوسعات الخاصة بك وقم باستعراضها أثناء العمل:
    • أنشئ سلسلة نصية "Object Oriented Programming" واطبع عدد الحروف المتحركة فيها بالتنسيق: Vowels: [count]
    • اطبع الأحرف الأولى (initials) لنفس السلسلة النصية بالتنسيق: Initials: [initials]
    • أنشئ قائمة من الأعداد الصحيحة [10, 20, 30]
    • اطبع نتيجة safeGet(1) بالتنسيق: Element at 1: [value]
    • اطبع نتيجة safeGet(5) بالتنسيق: Element at 5: [value]
    • اطبع ما إذا كانت القائمة تحتوي على عناصر متعددة بالتنسيق: Has multiple: [true/false]

تذكر أنه داخل التوسعة، يشير this إلى الكائن (instance) الذي يتم استدعاء الطريقة عليه. ستجعل التوسعات الخاصة بك هذه الأنواع تبدو وكأن هذه الطرق مدمجة فيها بالفعل!

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

Vowels: 9
Initials: OOP
Element at 1: 20
Element at 5: null
Has multiple: true

ورقة مرجعية

تسمح لك طرق التوسعة (Extension methods) بإضافة وظائف جديدة إلى الأنواع الموجودة دون تعديل الكود المصدري الخاص بها أو استخدام الوراثة.

بناء الجملة لإنشاء توسعة هو extension ExtensionName on Type:

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

بمجرد تعريفها، يمكن استدعاء طرق التوسعة على مثيلات (instances) من ذلك النوع كما لو كانت مدمجة فيه:

print('hello'.reverse());        // olleh
print('radar'.isPalindrome);     // true

تعمل التوسعات على أي نوع، بما في ذلك الأنواع المدمجة، وفئاتك الخاصة، والأنواع العامة (generic types):

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

print(5.squared());                    // 25
print([1, 2, 3].secondOrNull);         // 2

داخل التوسعة، يشير this إلى المثيل الذي يتم استدعاء الطريقة عليه.

جرّب بنفسك

import 'extensions.dart';

void main() {
  // إنشاء السلسلة النصية
  String text = "Object Oriented Programming";

  // TODO: اطبع عدد الأحرف الصوتية باستخدام countVowels()
  // التنسيق: Vowels: [count]

  // TODO: اطبع الأحرف الأولى باستخدام الـ getter المسمى initials
  // التنسيق: Initials: [initials]

  // إنشاء قائمة الأعداد الصحيحة
  List<int> numbers = [10, 20, 30];

  // TODO: اطبع نتيجة safeGet(1)
  // التنسيق: Element at 1: [value]

  // TODO: اطبع نتيجة safeGet(5)
  // التنسيق: Element at 5: [value]

  // TODO: اطبع ما إذا كانت القائمة تحتوي على عناصر متعددة
  // التنسيق: Has multiple: [true/false]
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming