حقن التبعية في Go: للمبتدئين — أمثلة ومبادئ

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

حقن التبعية في Go: للمبتدئين



حقن التبعية (DI)، أو Dependency Injection، هو أحد أنماط التصميم الرئيسية التي تساعد في كتابة كود نظيف وقابل للاختبار والصيانة. في عالم Go، حيث البساطة والوضوح هما الأهم، يتيح DI تجنب إنشاء "كائنات ضخمة" وروابط صلبة بين المكونات. في هذه المقالة، سنشرح ما هو حقن التبعية في Go، ولماذا هو ضروري، وكيفية تطبيقه عمليًا باستخدام أمثلة بسيطة وواضحة.



ما هو حقن التبعية؟



تخيل أنك تكتب برنامجًا يرسل رسائل ترحيبية للمستخدمين. على الأرجح، سيبدو الكود الخاص بك كالتالي:



package main

import "fmt"

type EmailService struct{}

func (e *EmailService) SendWelcomeEmail(userEmail string) { fmt.Printf("إرسال بريد إلكتروني إلى %s", userEmail)}

type UserController struct { emailService *EmailService}

func NewUserController() *UserController { return &UserController{ emailService: &EmailService{}, // رابط صلب }}

func (uc *UserController) RegisterUser(email string) { // ... منطق التسجيل uc.emailService.SendWelcomeEmail(email)}

func main() { controller := NewUserController() controller.RegisterUser("user@example.com")}


مشكلة هذا الكود هي أن UserController يقوم بإنشاء مثيل EmailService بنفسه. وهذا ما يسمى الرابط الصلب. إذا أردت استبدال EmailService بـ SmsService مثلًا، أو استخدام كائن وهمي للاختبار، فسيتعين عليك تغيير الكود داخل UserController.



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



حقن التبعية اليدوي عبر المنشئ



أبسط وأكثر طريقة شائعة لـ DI في Go هي تمرير التبعيات عبر دالة المنشئ. دعنا نعيد كتابة مثالنا:



package main

import "fmt"

// تعريف واجهة للمرونةtype Notifier interface { Send(to string, message string) error}

type EmailService struct{}

func (e *EmailService) Send(to string, message string) error { fmt.Printf("بريد إلكتروني إلى %s: %s", to, message) return nil}

type SmsService struct{}

func (s *SmsService) Send(to string, message string) error { fmt.Printf("رسالة نصية إلى %s: %s", to, message) return nil}

type UserController struct { notifier Notifier // التبعية عبر الواجهة}

// NewUserController — منشئ يقبل التبعيةfunc NewUserController(n Notifier) *UserController { return &UserController{ notifier: n, }}

func (uc *UserController) RegisterUser(email string) { // ... منطق الأعمال uc.notifier.Send(email, "مرحبًا بك!")}

func main() { // حقن التبعية يدويًا emailNotifier := &EmailService{} controller := NewUserController(emailNotifier) controller.RegisterUser("user@example.com")

// تغيير التنفيذ بسهولة smsNotifier := &SmsService{} controller2 := NewUserController(smsNotifier) controller2.RegisterUser("+123456789")}


ما الذي تغير؟

  • قمنا بتعريف واجهة Notifier. الآن UserController لا يعرف أي خدمة تُستخدم — بريد إلكتروني أم رسالة نصية.
  • المنشئ NewUserController يقبل Notifier كوسيط. هذا هو حقن التبعية.
  • في main، نقرر نحن أي تنفيذ نمرره.


هذا النهج يجعل الكود مرنًا وقابلًا للاختبار ومتوافقًا مع مبدأ انعكاس التحكم (Inversion of Control).

المدونات

توصيات الكتب