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

حقن التبعية

جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 57 من 91.

في الدرس السابق، رأيت كيف يسمح التركيب للكائن Car باحتواء كائن Engine. لكن توجد مشكلة في إنشاء التبعيات داخل الصنف، إذ يجعل ذلك الشيفرة جامدة ويصعب اختبارها. يحل حقن التبعية هذه المشكلة من خلال تمرير التبعيات من الخارج بدلًا من إنشائها داخليًا.

قارن بين هذين الأسلوبين:

<?php
// بدون DI - التبعية تُنشأ داخليًا
class Car {
    private Engine $engine;
    
    public function __construct() {
        $this->engine = new Engine(); // مقترن بإحكام
    }
}

// مع DI - التبعية تُمرَّر
class Car {
    public function __construct(private Engine $engine) {}
}

$engine = new Engine();
$car = new Car($engine); // محقونة من الخارج

النهج الثاني هو حقن التبعية. لا تنشئ Car محركها الخاص - بل تستقبل محركًا. لهذا التغيير البسيط فوائد قوية: يمكنك تمرير أنواع مختلفة من المحركات، وتبديل التطبيقات للاختبار، وتصبح الفئة أكثر مرونة.

لمزيد من المرونة، احقن الواجهات بدلًا من الفئات الملموسة:

<?php
interface EngineInterface {
    public function start(): string;
}

class Car {
    public function __construct(private EngineInterface $engine) {}
    
    public function start(): string {
        return $this->engine->start();
    }
}

الآن تعمل Car مع أي فئة تطبّق EngineInterface - محركًا يعمل بالوقود، أو محركًا كهربائيًا، أو كائنًا وهميًا للاختبار. هذا الفصل هو السبب في أن حقن التبعية أساسي لكتابة تطبيقات PHP قابلة للصيانة والاختبار.

challenge icon

التحدي

سهل

لنبنِ نظامًا للإشعارات يوضح قوة dependency injection. بدلًا من ترميز طريقة إرسال الرسائل بشكل ثابت، ستنشئ نظامًا مرنًا يمكن فيه تبديل طريقة التسليم بسهولة.

ستنظم الشيفرة عبر أربعة ملفات:

  • MessageSenderInterface.php: Define واجهة باسم MessageSenderInterface تحتوي على method واحدة هي send(string $recipient, string $message): string. تنشئ هذه الواجهة العقد الذي يجب على أي مُرسِل رسائل اتباعه.
  • EmailSender.php: Create class باسم EmailSender يقوم بـ implements MessageSenderInterface. أدرِج ملف الواجهة. يجب أن تُعيد method send() القيمة "Email to [recipient]: [message]".
  • NotificationService.php: Create class باسم NotificationService يتلقى dependency الخاصة به عبر constructor. أدرِج ملف الواجهة. يجب أن يقوم class بما يلي:
    • Accept قيمة من نوع MessageSenderInterface في constructor الخاص به باستخدام constructor promotion
    • Have method باسم notify(string $recipient, string $message) تقوم بـ delegates إلى المُرسِل injected وتُعيد نتيجته
    لاحظ كيف يعتمد NotificationService على الواجهة، وليس على class ملموس. وهذا يعني أنه يمكنك Inject أي مُرسِل يقوم بـ implements الواجهة من دون تغيير الخدمة.
  • main.php: أدرِج ملفي EmailSender وNotificationService. ستتلقى مُدخلين: recipient وmessage. أنشئ EmailSender، ثم Injectه في NotificationService، وبعد ذلك استدعِ notify() واطبع النتيجة.

يحافظ هذا النمط على مرونة NotificationService. فهو يعمل مع أي مُرسِل تقوم بحقنه، سواء كان ذلك البريد الإلكتروني أو الرسائل النصية القصيرة SMS أو شيئًا لم تبنه بعد. لا تنشئ الخدمة dependencies الخاصة بها؛ بل تتلقاها من الخارج outside.

جرّب بنفسك

<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';

// قراءة المدخلات
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));

// TODO: أنشئ كائن EmailSender
// TODO: احقنه في NotificationService
// TODO: استدعِ notify() واطبع النتيجة

?>
quiz iconاختبر نفسك

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

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت