في لغة برمجة دارت (Dart)، نجد نوعًا خاصًا من الـ Constructors يُعرف بالـ “Factory Constructor”، وهو نوع من الـ Constructors يوفر مرونة إضافية في عملية إنشاء الكائنات. حتى الآن، تعلمنا كيفية استخدام الـ “Generative Constructors” الذي يقوم بإنشاء نسخة جديدة من الكلاس فقط. ومع ذلك، يمكن للـ “Factory Constructor” أن يُستخدم لإنشاء نسخة من الكلاس نفسه، أو حتى لإنشاء نسخة من كلاس فرعي. بالإضافة إلى ذلك، يُمكن استخدامه أيضًا لإرجاع نسخة مخبأة (Cached Instance) من الكلاس.
قواعد الـ Factory Constructor في دارت:
- يجب أن يُرجع الـ Factory Constructor نسخة من الكلاس أو الكلاس الفرعي.
- لا يمكن استخدام كلمة “this” داخل الـ Factory Constructor.
- يمكن أن يكون الـ Factory Constructor بأسماء أو بدون أسماء ويمكن استدعاؤه كأي Constructor عادي.
- لا يمكن للـ Factory Constructor الوصول إلى أعضاء الكلاس.
مثال 1: بدون الـ Factory Constructor
في هذا المثال، لدينا كلاس تسمى (Area) يحتوي على خصائص نهائية (final) هي الطول والعرض والمساحة. عند تمرير الطول والعرض إلى الـ Constructor، يتم حساب المساحة وتخزينها في خاصية المساحة.
class Area { final int length; final int breadth; final int area; // Initializer list const Area(this.length, this.breadth) : area = length * breadth; } void main() { Area area = const Area(10, 20); print("المساحة هي: ${area.area}"); // لاحظ أن هنا قيمة سالبة Area area2 = const Area(-10, 20); print("المساحة هي: ${area2.area}"); }
نتيجة الكود:
المساحة هي: 200 المساحة هي: -200
في هذا المثال، تم إنشاء كائنين من الكلاس (Area)، الأول بقيم طول وعرض إيجابية والثاني بقيمة طول سالبة. يُلاحظ أن الكائن الثاني لديه قيمة سالبة للمساحة، وذلك بسبب أننا لم نُعالج القيم المدخلة في الـ Constructor.
مثال 2: باستخدام الـ Factory Constructor
في هذا المثال، يتم استخدام الـ Factory Constructor للتحقق من صحة القيم المدخلة. إذا كانت القيمة صالحة، فسيتم إرجاع نسخة جديدة من الكلاس. وإذا كانت القيمة غير صالح، فسيتم ظهور استثناء (Exception).
class Area { final int length; final int breadth; final int area; // private constructor const Area._internal(this.length, this.breadth) : area = length * breadth; // Factory constructor factory Area(int length, int breadth) { if (length < 0 || breadth < 0) { throw Exception("يجب أن تكون قيمة الطول والعرض إيجابية"); } // إعادة التوجيه إلى الـ Constructor الخاص return Area._internal(length, breadth); } } void main() { // هذا يعمل Area area = Area(10, 20); print("المساحة هي: ${area.area}"); // لاحظ أن هنا قيمة سالبة Area area2 = Area(-10, 20); print("المساحة هي: ${area2.area}"); }
نتيجة الكود:
المساحة هي: 200 Unhandled exception: Exception: يجب أن تكون قيمة الطول والعرض إيجابية
في هذا المثال، أُنشئ كائنين من كلاس (Area)، الأول بقيم طول وعرض إيجابية والثاني بقيمة طول سالبة. يتم ظهور استثناء (Exception) في الكائن الثاني بسبب عدم صحة القيم المدخلة في الـ Constructor.
مثال 3: الـ Factory Constructor في دارت
في هذا المثال، يوجد كلاس يسمى (Person) يحتوي على خاصيتين هما الاسم الأول والاسم الأخير، ويحتوي على Constructor عادي (Generative Constructor) والـ Factory Constructor. الـ Factory Constructor يقوم بإنشاء كائن من كلاس باستخدام Map.
class Person { String firstName; String lastName; // constructor Person(this.firstName, this.lastName); // factory constructor Person.fromMap factory Person.fromMap(Map<String, Object> map) { final firstName = map['firstName'] as String; final lastName = map['lastName'] as String; return Person(firstName, lastName); } } void main() { // إنشاء كائن شخص final person = Person('جون', 'دو'); // إنشاء كائن شخص من Map final person2 = Person.fromMap({'firstName': 'هاري', 'lastName': 'بوتر'}); // طباعة الاسم الأول والأخير print("من الـ Constructor: ${person.firstName} ${person.lastName}"); print("من الـ Factory Constructor: ${person2.firstName} ${person2.lastName}"); }
نتيجة الكود:
من الـ Constructor: جون دو من الـ Factory Constructor: هاري بوتر
في الدالة الرئيسية (main)، تم إنشاء كائنين، الأول باستخدام الـ Constructor العام (Generative Constructor) والثاني باستخدام الـ Factory Constructor، وتم طباعة الاسم الأول والأخير لكل كائن.
المثال 4: الـ Factory Constructor في دارت
في هذا المثال، دعونا ننظر في كلاس يُدعى “Shape” (الشكل) مع كلاسين فرعيين، “Circle” (الدائرة) و “Rectangle” (المستطيل). كل كلاس فرعي لديه الـ Factory Constructor خاص به لإنشاء مثيلات من الشكل المُقابل.
abstract class Shape { void printArea(); } class Circle implements Shape { final double radius; Circle(this.radius); factory Circle.fromDiameter(double diameter) { return Circle(diameter / 2); } @override void printArea() { final area = 3.14 * radius * radius; print('مساحة الدائرة: $area'); } } class Rectangle implements Shape { final double length; final double width; Rectangle(this.length, this.width); factory Rectangle.fromSides(double side1, double side2) { return Rectangle(side1, side2); } @override void printArea() { final area = length * width; print('مساحة المستطيل: $area'); } } void main() { final circle = Circle.fromDiameter(10); circle.printArea(); final rectangle = Rectangle.fromSides(5, 8); rectangle.printArea(); }
نتيجة الكود:
مساحة الدائرة: 78.5 مساحة المستطيل: 40.0
يُظهر هذا المثال كيف يمكن استخدام الـ Factory Constructor في لغة دارت لإنشاء أشكال مختلفة باستخدام منطق تهيئة مختلف استنادًا إلى المعلمات المقدمة.
إن الـ Factory Constructor في دارت يُمثل أحد الأدوات القوية والمرنة في إنشاء الكائنات وإدارتها. يسمح هذا النوع من الـ Constructors بتحقيق سيناريوهات مختلفة لإنشاء الكائنات بناءً على الحاجة. سواء كنت تحتاج إلى إنشاء نسخ جديدة من الكلاس أو إعادة استخدام نسخ مخبأة، يُمكن للـ Factory Constructor أن يكون أداة قوية في مجموعة أدواتك لتطوير تطبيقات دارت بشكل أكثر كفاءة.