الـ Constructors غير المتزامنة
جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 79 من 110.
لا يمكن تمييز المنشئات (Constructors) في Dart كـ async - يجب أن تعيد مثيلاً (instance) من الفئة فوراً، وليس Future. إذاً، كيف تنشئ كائنات تحتاج إلى تهيئة غير متزامنة (asynchronous initialization)، مثل تحميل البيانات من قاعدة بيانات أو جلب الإعدادات؟
الحل هو استخدام طريقة مصنع ثابتة غير متزامنة (static async factory method). اجعل المنشئ (constructor) خاصاً (أو بسيطاً)، وقم بتوفير طريقة ثابتة (static method) تقوم بالعمل غير المتزامن وتعيد Future من فئتك:
class UserProfile {
final String name;
final int age;
UserProfile._(this.name, this.age); // منشئ خاص
static Future<UserProfile> create(String userId) async {
// محاكاة جلب البيانات
await Future.delayed(Duration(seconds: 1));
return UserProfile._('Alice', 25);
}
}
Future<void> main() async {
var user = await UserProfile.create('123');
print(user.name); // Alice
}يقوم المُنشئ الخاص UserProfile._ بمعالجة عملية إنشاء الكائن الفعلية، بينما يقوم الأسلوب الثابت create() بمعالجة كافة الأعمال غير المتزامنة. يستخدم المستدعون await للحصول على الكائن الذي تم تهيئته بالكامل.
يضمن هذا النمط أن يكون الكائن الخاص بك جاهزاً تماماً للاستخدام بمجرد حصولك عليه - لا توجد حالات تمت تهيئتها جزئياً أو بيانات مفقودة. يُستخدم هذا النمط بشكل شائع للفئات التي تحتاج إلى تحميل الموارد، أو الاتصال بالخدمات، أو إجراء أي إعداد يستغرق وقتاً.
التحدي
سهللنقم ببناء محمل إعدادات (configuration loader) يوضح كيفية التعامل مع التهيئة غير المتزامنة (async initialization) في الأصناف (classes)! نظرًا لأن المنشئات (constructors) لا يمكن أن تكون غير متزامنة، ستستخدم نمط "static factory method" لإنشاء كائنات تحتاج إلى وقت "لتحميل" بياناتها.
ستقوم بتنظيم الكود الخاص بك في ملفين:
config_loader.dart: قم بإنشاء صنفConfigLoaderيحاكي تحميل إعدادات التكوين بشكل غير متزامن. يجب أن يحتوي الصنف الخاص بك على:- ثلاثة حقول نهائية (final fields):
appName(String)، وversion(String)، وmaxUsers(int) - منشئ خاص (private constructor) يقوم بتهيئة جميع الحقول الثلاثة
- دالة مصنع ثابتة غير متزامنة (static async factory method) تسمى
loadتأخذ معاملString configIdوتعيدFuture<ConfigLoader>. يجب أن تقوم هذه الدالة بما يلي:- استخدام
Future.delayed()مع 100 مللي ثانية لمحاكاة وقت التحميل - توليد قيم الإعدادات بناءً على
configId:appName: الـ configId مع إلحاق" App"بهversion:"1.0."متبوعًا بطول configIdmaxUsers: طول configId مضروبًا في 10
- إرجاع مثيل (instance) جديد من
ConfigLoaderبهذه القيم
- استخدام
- دالة
displayInfo()تطبع الإعدادات بهذا التنسيق:Config: [appName] v[version] (max [maxUsers] users)
- ثلاثة حقول نهائية (final fields):
main.dart: قم باستيراد محمل الإعدادات الخاص بك واستعرض نمط التهيئة غير المتزامنة:- اطبع
Loading configurations... - قم بتحميل إعداد باستخدام configId بقيمة
"Production"واستدعِ دالةdisplayInfo()الخاصة به - قم بتحميل إعداد باستخدام configId بقيمة
"Dev"واستدعِ دالةdisplayInfo()الخاصة به - اطبع
All configs loaded!
- اطبع
لاحظ كيف تتعامل الدالة الثابتة load مع جميع العمليات غير المتزامنة، بينما يقوم المنشئ الخاص ببساطة بتعيين القيم. يستخدم المستدعون await للحصول على كائن تمت تهيئته بالكامل!
المخرجات المتوقعة:
Loading configurations...
Config: Production App v1.0.10 (max 100 users)
Config: Dev App v1.0.3 (max 30 users)
All configs loaded!ورقة مرجعية
لا يمكن تمييز المشيدات (Constructors) في Dart بكلمة async لأنها يجب أن تعيد نسخة من الفئة (class) فوراً، وليس Future.
لإنشاء كائنات تتطلب تهيئة غير متزامنة، استخدم طريقة مصنع ثابتة غير متزامنة (static async factory method):
class UserProfile {
final String name;
final int age;
UserProfile._(this.name, this.age); // Private constructor
static Future<UserProfile> create(String userId) async {
// Perform async work here
await Future.delayed(Duration(seconds: 1));
return UserProfile._('Alice', 25);
}
}الاستخدام:
Future<void> main() async {
var user = await UserProfile.create('123');
print(user.name); // Alice
}يتولى المشيد الخاص (المميز بـ _) عملية إنشاء الكائن، بينما تتولى الطريقة الثابتة (static method) العمل غير المتزامن. هذا يضمن تهيئة الكائنات بالكامل قبل استخدامها.
جرّب بنفسك
import 'config_loader.dart';
void main() async {
// TODO: اطبع "Loading configurations..."
// TODO: قم بتحميل إعداد بـ configId "Production"
// واستدعِ دالة displayInfo() الخاصة به
// TODO: قم بتحميل إعداد بـ configId "Dev"
// واستدعِ دالة displayInfo() الخاصة به
// TODO: اطبع "All configs loaded!"
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس 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)3خصائص الـ 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أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع