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

الـ 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 للحصول على الكائن الذي تم تهيئته بالكامل.

يضمن هذا النمط أن يكون الكائن الخاص بك جاهزاً تماماً للاستخدام بمجرد حصولك عليه - لا توجد حالات تمت تهيئتها جزئياً أو بيانات مفقودة. يُستخدم هذا النمط بشكل شائع للفئات التي تحتاج إلى تحميل الموارد، أو الاتصال بالخدمات، أو إجراء أي إعداد يستغرق وقتاً.

challenge icon

التحدي

سهل

لنقم ببناء محمل إعدادات (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." متبوعًا بطول configId
        • maxUsers: طول configId مضروبًا في 10
      • إرجاع مثيل (instance) جديد من ConfigLoader بهذه القيم
    • دالة displayInfo() تطبع الإعدادات بهذا التنسيق: Config: [appName] v[version] (max [maxUsers] users)
  • 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!"
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming