يُنصح المطورون الذين يبنون تطبيقات Laravel بالتخلي عن الكلمة المفتاحية new داخل المتحكمات (controllers) والتحول إلى نمط Factory Method. فمن خلال نقل عملية إنشاء الكائنات إلى مصانع (factories) مخصصة، يصبح الكود أسهل في التوسع والاختبار والصيانة—وهي فوائد تزداد أهميتها مع نمو المشاريع لتتجاوز مجرد عدد قليل من نقاط النهاية (endpoints).
لماذا تُعد الكلمة المفتاحية new عبئاً خفياً
عندما يحتوي المتحكم على سطر مثل
$processor = new StripePaymentProcessor($config);
يصبح المتحكم الآن مرتبطاً ارتباطاً وثيقاً (tightly coupled) بفئة StripePaymentProcessor. وكل مكان يحتاج إلى معالج دفع سيكرر هذا السطر، مما يؤدي إلى تشتيت منطق الإنشاء عبر قاعدة الكود. وإذا تطلب المعالج لاحقاً مسجلاً (logger)، أو مدير ذاكرة مؤقتة (cache manager)، أو تنسيق إعدادات مختلف، فيجب تحديث كل نقطة من تلك النقاط. والنتيجة هي كود هش يقاوم التغيير ويصعب محاكاته (mock) في اختبارات الوحدة (unit tests).
تدخل نمط Factory Method
يستبدل نمط Factory Method عملية الإنشاء المباشر بطريقة (method) تُرجع مثيلاً (instance) لواجهة (interface) معينة. يعتمد المتحكم على الواجهة، بينما تعرف فئة المصنع (factory class) أي فئة ملموسة (concrete class) يجب بناؤها وكيفية تجميع تبعياتها. يعيش كل منطق الإنشاء في مكان واحد، لذا فإن إضافة تبعية جديدة أو تبديل التنفيذ لا يتطلب سوى تعديل المصنع فقط.
المزايا الأساسية
- الارتباط الضعيف (Loose coupling) – تعمل المتحكمات بناءً على التجريدات (abstractions)، وليس الفئات الملموسة.
- الإنشاء المركزي – يتطلب تغيير عملية البناء تعديل ملف واحد فقط.
- قابلية الاختبار – يمكن استبدال المصانع بنماذج وهمية (stubs) أو محاكاة (mocks)، مما يسمح باختبارات معزولة للمتحكمات.
- مبدأ الفتح/الإغلاق (Open/Closed Principle) – يمكن إضافة ميزات جديدة (مثل بوابة دفع جديدة) دون المساس بكود المتحكم الحالي.
تشبيه بمقهى
تخيل باريستا (صانع قهوة) يجب عليه طحن الحبوب، وتبخير الحليب، وصب الماء يدوياً لكل طلب، باستخدام سلسلة طويلة من جمل if-else. إذا تغيرت وصفة اللاتيه، يجب على كل باريستا إعادة تعلم الخطوات. أما آلة القهوة التي تستقبل نوع الطلب وتتولى عملية التحضير داخلياً، فهي تحل المشكلة: يخبر الباريستا الآلة فقط بما هو مطلوب، وتقوم الآلة بتغليف (encapsulate) جميع الخطوات. تحديث الوصفة الآن يعني ضبط الآلة، وليس كل باريSTA.
Laravel تعتمد بالفعل على المصانع
توضح مكونات Laravel الأساسية هذا النمط في الواقع:
- قاعدة البيانات – تقرر
ConnectionFactoryما إذا كانت ستبني اتصال MySQL أو PostgreSQL. - الطوابير (Queues) – تنشئ
QueueManagerبرامج تشغيل (drivers) لـ Redis أو SQS أو غيرها من الأنظمة الخلفية. - نظام الملفات – تنتج
FilesystemManagerمثيلات لأقراص محلية أو S3. - البريد – تقوم
MailManagerبحل (resolve) برامج تشغيل SMTP أو Mailgun أو غيرها.
إذا كان إطار العمل يثق في المصانع لهذه الخدمات الحيوية، فيجب أن يتبع الكود المخصص نفس النهج.
متى تستخدم المصنع
استخدم المصنع عندما:
- تتضمن عملية إنشاء الكائن خطوات إعداد متعددة أو خدمات خارجية.
- وجود عدة عمليات تنفيذ قابلة للتبديل (بوابات دفع مختلفة، مزودي تخزين، إلخ).
- من المتوقع أن تزداد قائمة عمليات التنفيذ الممكنة.
تجنب استخدام المصنع عندما:
- يكون الإنشاء عبارة عن استدعاء
newواحد وغير قابل للتغيير دون أي إعدادات إضافية. - سيتم استخدام عملية تنفيذ واحدة فقط دائماً، مما يجعل التجريد الإضافي عبئاً غير ضروري.
جولة سريعة: إعادة هيكلة (refactoring) متحكم الدفع
- تعريف واجهة (interface) –
PaymentProcessorInterfaceمع طريقةprocess(array $data). - تنفيذ الفئات الملموسة –
StripePaymentProcessorوPayPalPaymentProcessorبحيث تلبي كل منهما الواجهة. - إنشاء مصنع –
PaymentProcessorFactoryمع طريقةmake(string $driver): PaymentProcessorInterface. في الداخل، استخدمswitchأو خريطة (map) لإرجاع الفئة المناسبة، مع حقن أي خدمات مطلوبة من حاوية الخدمات (service container) في Laravel. - حقن المصنع – في منشئ (constructor) المتحكم، استخدم تلميح النوع (type-hint) لـ
PaymentProcessorFactory. سيقوم Laravel بحلها تلقائياً. - استخدام المصنع –
$processor = $this->processorFactory->make('stripe'); $processor->process($request->all());
الآن لا يذكر المتحكم أبداً كلمة new أو أي معالج ملموس. إضافة بوابة جديدة تعني إنشاء فئة وتوسيع خريطة المصنع—دون إجراء أي تغييرات على المتحكم.
العيوب المحتملة وكيفية التخفيف منها
يتمثل الانتقاد الرئيسي للمصانع (factories) في إضافة طبقة غير مباشرة (indirection)، مما قد يجعل الكود يبدو مسهباً (verbose) عند التعامل مع الكائنات البسيطة. كما أن الإفراط في هندسة (Over-engineering) خدمة بسيطة قد يؤدي إلى تضخم قاعدة الكود دون فائدة حقيقية. يكمن السر في تقييم مدى تعقيد عملية الإنشاء: فإذا كان الكائن مجرد حامل بيانات بسيط (plain data holder) دون أي تبعيات (dependencies)، فقد يكون استخدام new المباشر أمراً مقبولاً. علاوة على ذلك، قد تتحول المصانع إلى مستودع للمنطق البرمجي غير ذي الصلة؛ لذا فإن إبقاء المصانع مركزة على إنشاء الكائنات وتفويض الإعدادات (configuration) إلى مزودي خدمة (service providers) مخصصين يضمن الحفاظ على وضوح الكود.
ما يجب مراقبته لاحقاً
- ارتباطات حاوية الخدمة (Service container bindings) – يمكن لحاوية Laravel حل المصانع تلقائياً إذا قمت بربط الواجهة (interface) بفئة المصنع (factory class) في مزود خدمة (service provider).
- الاكتشاف التلقائي (Auto-discovery) – توفر بعض الحزم مصانعها الخاصة؛ لذا كن حذراً من تضارب الأسماء (naming collisions).
- استراتيجيات الاختبار (Testing strategies) – عند إجراء اختبارات الوحدة (unit testing) للمتحكمات (controllers)، استبدل المصنع بـ mock يعيد معالجاً وهمياً (stubbed processor)، مما يضمن بقاء الاختبارات سريعة ومعزولة.
الخلاصة
إن استبدال استدعاءات new المشتتة بمصنع موضوع في مكان مناسب يساهم في مركزية عملية الإنشاء، ويقلل من الاقتران (coupling)، ويجعل الكود المخصص يتماشى مع فلسفة التصميم الخاصة بـ Laravel. بالنسبة للفرق التي تتوقع النمو — مثل إضافة مزودي دفع جدد، أو أنظمة تخزين خلفية (storage back-ends)، أو أي مكونات بنمط الإضافات (plug-in-style) — فإن نمط Factory Method هو استثمار منخفض التكلفة يؤتي ثماره من حيث المرونة والثقة. ستشكر نفسك المستقبلية، وكل من يرث هذا الكود، على هذا القرار.
