مقدمة: لماذا يعتبر حقن التبعية مهماً في Go؟
\n\nGo (Golang) هي لغة تشتهر ببساطتها وأدائها. ومع ذلك، عندما يبدأ مشروعك في النمو، يمكن أن يتحول الكود بسرعة إلى "سباغيتي"، حيث تكون الدوال والهياكل مرتبطة ببعضها البعض بشكل صارم. هنا يأتي دور حقن التبعية (DI).
\n\nبالنسبة للمطورين المبتدئين، قد يبدو DI مفهوماً معقداً من عالم Java أو C#. لكن في Go، يتم تنفيذ هذا النمط بأناقة وبدون أطر عمل. في هذه المقالة، سنشرح ما هو حقن التبعية، ولماذا نحتاجه، وكيفية تطبيقه بشكل صحيح في Go مع أمثلة عملية.
\n\nإذا كنت تريد كتابة كود قابل للاختبار ومرن وقابل للصيانة في Golang، فهذه المقالة لك.
\n\nما هو حقن التبعية (DI)؟
\n\nحقن التبعية هو نمط تصميم يحصل فيه الكائن على تبعياته (كائنات أخرى يحتاجها للعمل) من الخارج، بدلاً من إنشائها بنفسه داخله.
\n\nبكلمات بسيطة: بدلاً من كتابة myService := MyService{} داخل الكود الخاص بك، تقوم "بحقن" نسخة جاهزة من myService في المُنشئ أو الدالة.
المشكلة بدون DI
\n\nتخيل أن لديك تطبيقاً لإرسال الإشعارات. بدون DI، قد يبدو الكود كالتالي:
\n\npackage main\n\nimport "fmt"\n\ntype EmailNotifier struct{}\n\nfunc (e EmailNotifier) Send(message string) {\n fmt.Println("إرسال بريد إلكتروني:", message)\n}\n\ntype UserService struct {\n notifier EmailNotifier // ارتباط صارم\n}\n\nfunc NewUserService() *UserService {\n return &UserService{\n notifier: EmailNotifier{}, // إنشاء التبعية داخلياً\n }\n}\n\nfunc (u *UserService) NotifyUser(message string) {\n u.notifier.Send(message)\n}\n\nfunc main() {\n service := NewUserService()\n service.NotifyUser("مرحباً!")\n}\n\n\nما المشكلة هنا؟
\n- \n
- ارتباط صارم:
UserServiceمرتبط بـEmailNotifier. إذا أردت استبدال البريد الإلكتروني بـ SMS، ستحتاج لتغيير كودUserService. \n - صعوبة الاختبار: لا يمكنك اختبار
UserServiceدون إرسال بريد إلكتروني حقيقي. هذا أمر بالغ الأهمية لاختبارات الوحدة. \n
الحل مع DI
\n\nالآن سنعيد كتابة نفس المثال باستخدام حقن التبعية:
\n\npackage main\n\nimport "fmt"\n\n// Notifier — واجهة تجرد إرسال الإشعارات\ntype Notifier interface {\n Send(message string)\n}\n\ntype EmailNotifier struct{}\n\nfunc (e EmailNotifier) Send(message string) {\n fmt.Println("إرسال بريد إلكتروني:", message)\n}\n\ntype SMSNotifier struct{}\n\nfunc (s SMSNotifier) Send(message string) {\n fmt.Println("إرسال SMS:", message)\n}\n\ntype UserService struct {\n notifier Notifier // تبعية عبر الواجهة\n}\n\n// NewUserService يستقبل Notifier من الخارج\nfunc NewUserService(n Notifier) *UserService {\n return &UserService{notifier: n}\n}\n\nfunc (u *UserService) NotifyUser(message string) {\n u.notifier.Send(message)\n}\n\nfunc main() {\n // حقن EmailNotifier\n emailService := NewUserService(EmailNotifier{})\n emailService.NotifyUser("مرحباً عبر البريد الإلكتروني!")\n\n // حقن SMSNotifier — دون تغيير UserService!\n smsService := NewUserService(SMSNotifier{})\n smsService.NotifyUser("مرحباً عبر SMS!")\n}\n\n\nالآن UserService لا يعرف أي مُشعِر يتم استخدامه بالضبط. هو فقط يستدعي دالة Send على الكائن المُمرر. هذا هو حقن التبعية.
المزايا الرئيسية لـ DI في Go
\n\n1. قابلية الاختبار (كائنات وهمية Mock)
\n\nيجعل DI الكود الخاص بك مثالياً لاختبارات الوحدة. يمكنك بسهولة استبدال التبعية الحقيقية بـ "كائن وهمي" (mock).
\n\npackage main\n\nimport "testing"\n\ntype MockNotifier struc