إعادة تعريف 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).
التحدي
سهللنقم ببناء مسجل دوال (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'
- إذا كان اسم الدالة يحتوي على
- استخراج اسم الدالة من الاستدعاء (سيكون عبارة عن Symbol مثل
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- اسم الطريقة كـ SymbolpositionalArguments- الوسائط التي تم تمريرها إلى الطريقة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() قم باستدعاء
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةالمكتبات والـ Importsمقدمة في الـ OOPالـ Classes مقابل الـ Objectsالكلمة المفتاحية thisالـ Methodsمتغيرات الـ Instanceأساسيات الـ Constructorمراجعة - آلة حاسبة بسيطة4أمان الـ Null
مقدمة في أمان الـ NullNullable مقابل Non-Nullableعاملا الـ ? و !الكلمة المفتاحية Late وأمان الـ Nullالعوامل المدركة للـ Nullأمان الـ Null في الـ Classesمراجعة - نظام ملف تعريف المستخدم7الفئات المجردة والواجهات
الفئات المجردةالدوال المجردةالواجهات في Dartالواجهات الضمنيةالتنفيذ مقابل التوسيعالواجهات المتعددةمراجعة - حاسبة الأشكال10المجموعات والأنماط العامة
نظرة عامة على List و Set و Mapالمجموعات آمنة النوعالفئات العامةالدوال العامةقيود الأنماط العامةIterable و Iteratorملخص - التخزين العام13مفاهيم OOP المتقدمة
التركيب مقابل الوراثة (Composition vs Inheritance)دوال التوسعة (Extension Methods)الأصناف القابلة للاستدعاء (Callable Classes)الأصناف المختومة (Sealed Classes - Dart 3)السجلات (Records - Dart 3)الأنماط والمطابقة (Patterns & Matching - 3.0)التعدادات مع الدوال (Enums with Methods)2المنشئات في Dart
المنشئ الافتراضيالمنشئات المسماةقوائم التهيئةالمنشئات الثابتةالمنشئات المصنعيةالمنشئات الموجهةملخص - بناء الأشكال5التغليف (Encapsulation)
الأعضاء العامة مقابل الخاصةاتفاقية البادئة _الخصوصية على مستوى المكتبةتعمق في Getters و Settersإخفاء المعلوماتمراجعة - سجلات الطلاب8Mixins
مقدمة إلى Mixinsإنشاء Mixinsاستخدام Mixins متعددةالكلمة المفتاحية on في MixinsMixin مقابل الوراثة (Inheritance)Mixin مقابل الواجهة (Interface)مراجعة - نظام الحيوانات (Animal System)11الدوال الخاصة
إعادة تعريف toString()إعادة تعريف hashCode و ==واجهة Comparableدالة call()إعادة تعريف noSuchMethodمراجعة - Custom Collection3خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticحقول Final و Constمتغيرات Lateالـ Static Methods والحقولدوال الـ Getters والـ Settersمراجعة - مدير الحساب البنكي6الوراثة
أساسيات الوراثةالكلمة المفتاحية superإعادة تعريف الدوال (Method Overriding)الوسم @overrideالكلمة المفتاحية final Classالدوال البانية والوراثةمراجعة - هيكلية الموظفين9تعدد الأشكال
أساسيات تعدد الأشكالتعدد الأشكال عبر الـ Interfacesالتحقق من النوع أثناء التشغيلالمعاملات is و asالكلمة المفتاحية Covariantملخص - معالج الدفع12البرمجة الكائنية غير المتزامنة (Async OOP)
Futures و async/awaitأساسيات الـ Streamsمتحكمات الـ Streamsالـ Constructors غير المتزامنةالعمليات غير المتزامنة في دوال الكلاسمراجعة - جالب البيانات (Data Fetcher)15أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع