مقدمة: الجدل الأبدي في عالم Go
\n\nGo — لغة حطمت القوالب. إنها لا تغفر التعقيد المفرط، ولكنها في الوقت نفسه تمنح المطور حرية لا تصدق. كل من يبدأ في الكتابة بلغة Go (أو ينتقل من Python أو Java) يصطدم عاجلاً أم آجلاً بحائط: «وكيف يكون الصواب؟ الكتابة مثل Java مع الفئات والوراثة أم مثل Haskell مع الدوال الخالية من الآثار الجانبية؟»
\n\nإفساد: Go ليست لغة كائنية التوجه بحتة ولا وظيفية بحتة. إنها براغماتية. لكن هذا لا يعني أنه لا يمكنك (ولا ينبغي عليك) استخدام كلا النموذجين. في هذه المقالة، سنتعرف على كيفية الجمع بين المنهج الوظيفي والكائني التوجه في Go بحيث يكون كودك قابلاً للقراءة والاختبار وسريعاً. لن نلقي محاضرات، بل سننظر إلى أمثلة حقيقية وأنماط مضادة.
\n\nإذا كنت تبحث عن أفضل ممارسات Go وتريد فهم كيفية كتابة كود نظيف بهذه اللغة — فأنت في المكان الصحيح.
\n\n1. المنهج الكائني التوجه في Go: بدون فئات ولكن بروح
\n\nيعتقد الكثيرون أنه بما أنه لا يوجد class في Go، فلا يوجد OOP فيها. هذه خرافة. البرمجة كائنية التوجه في Go تقوم على ركيزتين: الهياكل (structs) والواجهات (interfaces). لا يوجد وراثة، ولكن يوجد تكوين (composition) الذي يعتبره مبتكرو اللغة أكثر مرونة.
الهياكل والطرق
\nيمكنك تعريف طرق لأي نوع. هذا هو أساس الكود كائني التوجه.
\n\npackage main\n\nimport "fmt"\n\n// تعريف واجهة (عقد)\ntype Notifier interface {\n Send(message string) error\n}\n\n// هيكل ينفذ الواجهة\ntype EmailNotifier struct {\n Email string\n}\n\n// طريقة لـ EmailNotifier\nfunc (e EmailNotifier) Send(message string) error {\n fmt.Printf("إرسال بريد إلكتروني إلى %s: %s\\n", e.Email, message)\n return nil\n}\n\n// هيكل آخر\ntype SMSNotifier struct {\n Phone string\n}\n\nfunc (s SMSNotifier) Send(message string) error {\n fmt.Printf("إرسال رسالة نصية إلى %s: %s\\n", s.Phone, message)\n return nil\n}\n\n// دالة تقبل أي Notifier (تعدد الأشكال)\nfunc AlertAll(notifiers []Notifier, msg string) {\n for _, n := range notifiers {\n n.Send(msg)\n }\n}\n\nfunc main() {\n notifiers := []Notifier{\n EmailNotifier{Email: "user@example.com"},\n SMSNotifier{Phone: "+1234567890"},\n }\n AlertAll(notifiers, "الخادم تعطل!")\n}\n\nمزايا OOP في Go:
\n- \n
- تعدد الأشكال عبر الواجهات. تكتب دالة
AlertAllمرة واحدة، وتعمل مع أي نوع ينفذNotifier. هذا ارتباط ضعيف. \n - الحالة. الهياكل مثالية لنماذج البيانات (User, Order, Config). \n
- التغليف. الحقول بحرف صغير — خاصة، بحرف كبير — عامة. بسيط وفعال. \n
العيوب:
\n- \n
- من السهل الانزلاق إلى «أسلوب Java»: إنشاء تسلسلات هرمية عميقة، على الرغم من أن Go لا يدعمها. \n
- قابلية التغيير (تغيير الحالة) تعقد الاختبار وتصحيح الأخطاء. \n
2. البرمجة الوظيفية في Go: النقاء من أجل السرعة
\n\nالأسلوب الوظيفي في Go ليس حساب لامدا، بل هو مجموعة من المبادئ: الحد الأدنى من الآثار الجانبية، عدم القابلية للتغيير، الدوال كوسائط. مع ظهور الأدوية العامة (Go 1.18+) أصبح هذا الأسلوب أقوى.
\n\nالدوال النقية وعدم القابلية للتغيير
\nالقاعدة الرئيسية للبرمجة الوظيفية: الدالة تستقبل البيانات، وتعيد بيانات جديدة، لكنها لا تغير أي شيء خارجها.
\n\npackage main\n\nimport "fmt"\n\n// سيء: تغيير الشريحة الواردة (أثر جانبي)\nfunc BadAddPrefix(users []string, prefix string) {\n for i := range users {\n users[i] = prefix + users[i]\n }\n}\n\n// جيد: إرجاع شريحة جديدة (دالة نقية)\