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

إعادة تعريف noSuchMethod

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

عندما تستدعي تابعاً (method) غير موجود في كائن ما، فإن Dart عادةً ما تطلق NoSuchMethodError. ومع ذلك، يمكنك تجاوز التابع الخاص noSuchMethod() لاعتراض هذه الاستدعاءات ومعالجتها بسلاسة.

لاستخدام noSuchMethod()، يجب أن يقوم الصنف (class) الخاص بك أيضاً بتنفيذ واجهة (interface) أو استخدام النوع dynamic.

تستقبل الطريقة كائن Invocation يحتوي على تفاصيل حول الاستدعاء الذي تمت محاولته:

class Flexible {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    var methodName = invocation.memberName.toString();
    print('Called: $methodName');
    return null;
  }
}

void main() {
  dynamic obj = Flexible();
  obj.anyMethod();      // تم استدعاء: Symbol("anyMethod")
  obj.anotherOne(42);   // تم استدعاء: Symbol("anotherOne")
}

يوفر كائن Invocation معلومات مفيدة حول الاستدعاء - يعطيك memberName اسم الطريقة كـ Symbol، ويحتوي positionalArguments على الوسائط الممرة، ويخبرك isMethod ما إذا كان استدعاء طريقة مقابل getter أو setter.

class Logger {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    print('Method: ${invocation.memberName}');
    print('Args: ${invocation.positionalArguments}');
    return 'handled';
  }
}

void main() {
  dynamic log = Logger();
  var result = log.save('data', 123);
  print(result);  // تمت المعالجة
}

هذه التقنية مفيدة لإنشاء كائنات وكيلة (proxy objects)، أو فئات وهمية (mock classes) للاختبار، أو بناء واجهات برمجة تطبيقات (APIs) مرنة تستجيب لاستدعاءات دوال عشوائية. ومع ذلك، استخدمها باعتدال لأنها تتجاوز التحقق من النوع وقت التجميع (compile-time type checking).

challenge icon

التحدي

سهل

لنقم ببناء مسجل دوال (method logger) يقوم باعتراض وتسجيل أي استدعاءات للدوال يتم إجراؤها على كائن! ستقوم بإنشاء فئة تستخدم noSuchMethod() لالتقاط معلومات حول استدعاءات الدوال غير المعرفة والاستجابة بذكاء بناءً على اسم الدالة.

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

  • method_logger.dart: أنشئ فئة MethodLogger تعترض الاستدعاءات للدوال غير المعرفة. يجب أن يقوم المسجل الخاص بك بعمل override لـ noSuchMethod() من أجل:
    • استخراج اسم الدالة من الاستدعاء (سيكون عبارة عن Symbol مثل Symbol("methodName") - ستحتاج إلى تحويله إلى سلسلة نصية واستخراج الاسم فقط)
    • طباعة اسم الدالة وأي وسائط موضعية (positional arguments) بهذا التنسيق: Called: [methodName] with args: [arguments]
    • إرجاع استجابة خاصة بناءً على اسم الدالة:
      • إذا كان اسم الدالة يحتوي على get، فقم بإرجاع السلسلة النصية 'data retrieved'
      • إذا كان اسم الدالة يحتوي على save، فقم بإرجاع السلسلة النصية 'data saved'
      • خلاف ذلك، قم بإرجاع السلسلة النصية 'unknown action'
  • main.dart: قم باستيراد المسجل الخاص بك وتوضيح كيفية تعامله مع استدعاءات الدوال غير المعرفة المختلفة. تذكر استخدام النوع dynamic حتى يسمح Dart باستدعاء دوال غير موجودة:
    • أنشئ مثيلاً من MethodLogger باستخدام متغير ديناميكي
    • قم باستدعاء getUserData('Alice', 42) عليه واطبع النتيجة المرجعة
    • قم باستدعاء saveRecord('important') عليه واطبع النتيجة المرجعة
    • قم باستدعاء processItem() عليه (بدون وسائط) واطبع النتيجة المرجعة

لاستخراج اسم الدالة من Symbol، يمكنك تحويله إلى سلسلة نصية ثم تحليل الاسم الموجود بين علامتي الاقتباس. على سبيل المثال، Symbol("myMethod").toString() يعطيك 'Symbol("myMethod")'.

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

Called: getUserData with args: [Alice, 42]
data retrieved
Called: saveRecord with args: [important]
data saved
Called: processItem with args: []
unknown action

ورقة مرجعية

تسمح لك الطريقة noSuchMethod() باعتراض الاستدعاءات للطرق التي لا توجد في الكائن. لاستخدامها، يجب أن تستخدم فئتك النوع dynamic أو تنفذ واجهة.

قم بإعادة تعريف noSuchMethod() للتعامل مع استدعاءات الطرق غير المعرفة:

class Flexible {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    var methodName = invocation.memberName.toString();
    print('Called: $methodName');
    return null;
  }
}

void main() {
  dynamic obj = Flexible();
  obj.anyMethod();      // Called: Symbol("anyMethod")
  obj.anotherOne(42);   // Called: Symbol("anotherOne")
}

يوفر كائن Invocation معلومات حول الاستدعاء:

  • memberName - اسم الطريقة كـ Symbol
  • positionalArguments - الوسائط التي تم تمريرها إلى الطريقة
  • isMethod - يشير إلى ما إذا كان الاستدعاء لطريقة مقابل كونه getter أو setter
class Logger {
  @override
  dynamic noSuchMethod(Invocation invocation) {
    print('Method: ${invocation.memberName}');
    print('Args: ${invocation.positionalArguments}');
    return 'handled';
  }
}

void main() {
  dynamic log = Logger();
  var result = log.save('data', 123);
  print(result);  // handled
}

هذه التقنية مفيدة لإنشاء كائنات وكيلة (proxy objects)، أو فئات وهمية (mock classes) للاختبار، أو واجهات برمجة تطبيقات مرنة، ولكن يجب استخدامها بحذر لأنها تتجاوز التحقق من النوع أثناء وقت التجميع.

جرّب بنفسك

import 'method_logger.dart';

void main() {
  // TODO: dynamic typing باستخدام MethodLogger قم بإنشاء مثيل من
  // يسمح استخدام dynamic باستدعاء طرق غير موجودة في وقت التجميع
  dynamic logger;
  
  // TODO: وقم بطباعة النتيجة getUserData('Alice', 42) قم باستدعاء
  
  // TODO: وقم بطباعة النتيجة saveRecord('important') قم باستدعاء
  
  // TODO: بدون وسائط وقم بطباعة النتيجة processItem() قم باستدعاء
  
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming