أنماط التصميم في Go: متى ولماذا نستخدمها

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

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

ابدأ الدورة

أنماط التصميم في Go: ليست "دينًا"، بل أداة

\n\n

عندما بدأت الكتابة بلغة Go لأول مرة، كان يلازمني شعور بأن كل هذه "مجموعة الأربعة" وأنماطهم الـ23 هي شيء من عالم Java وC++. Go بسيطة وموجزة - فلماذا نحتاج هذه التعقيدات؟ ولكن كلما تعمقت في المشاريع الحقيقية (الخدمات المصغرة، أدوات سطر الأوامر، معالجات API)، كنت أجد نفسي أفكر: "هنا سيكون مفيدًا استخدام طريقة المصنع" أو "هذه الكومة من if-else يمكن استبدالها باستراتيجية".

\n\n

أنماط التصميم في Go ليست عقيدة ولا محاولة لفرض البرمجة كائنية التوجه على لغة وظيفية. إنها حلول مثبتة للمشكلات النمطية. تساعد في جعل الكود قابلًا للاختبار ومرنًا ومفهومًا للزملاء. ونعم، تفرض Go بصمتها: بسبب غياب الوراثة التقليدية والأدوية العامة (حتى الإصدار 1.18)، تتحول بعض الأنماط ويصبح بعضها الآخر زائدًا عن الحاجة.

\n\n

في هذه المقالة، لن نعدد جميع الأنماط الـ23. بدلاً من ذلك، سنحلل 5 من أكثر الأنماط طلبًا في Golang والتي ستفيدك حقًا في المشاريع الفعلية. سترى متى يجب استخدامها، ومتى يجب الابتعاد عنها دون تردد.

\n\n

نمط Singleton في Go: ليس مجرد مفرد، بل مفرد على الطريقة الغووية

\n\n

يضمن Singleton أن يكون للهيكل مثيل واحد فقط. في Go، يُستخدم هذا غالبًا للإعدادات، مجموعات الاتصالات، أو مسجلات الأحداث. لكن هناك فارقًا: في Go لا توجد كلاسات ثابتة، ولكن يوجد sync.Once - الأداة المثالية للتهيئة البطيئة.

\n\n

المشكلة بدون Singleton

\n

تخيل أن لديك هيكل Config يقرأ الإعدادات من ملف. إذا أنشأت كل حزمة مثيلها الخاص، فإما أن تقرأ الملف 10 مرات (بطيء) أو تحصل على حالة سباق.

\n\n

الحل باستخدام Singleton

\n
package config\n\nimport (\n    "sync"\n    "encoding/json"\n    "os"\n)\n\ntype Config struct {\n    Port int    `json:"port"`\n    Env  string `json:"env"`\n}\n\nvar (\n    instance *Config\n    once     sync.Once\n)\n\nfunc GetConfig() *Config {\n    once.Do(func() {\n        file, _ := os.Open("config.json")\n        defer file.Close()\n        instance = &Config{}\n        json.NewDecoder(file).Decode(instance)\n    })\n    return instance\n}
\n\n

متى تستخدم: عندما تحتاج إلى ضمان تفرد المثيل (الإعدادات، التخزين المؤقت، المصانع).

\n

متى لا تستخدم: لكل شيء. Singleton هو حالة عامة تعقد الاختبار. إذا كان بإمكانك تمرير التبعية عبر المُنشئ - فافعل ذلك.

\n\n

نمط Factory: عندما يصبح إنشاء الكائنات معقدًا

\n\n

Factory Method وAbstract Factory يتعلقان بتغليف منطق إنشاء الكائنات. في Go، هذا مهم بشكل خاص لأنه ليس لدينا مُنشئات بالمعنى التقليدي. بدلاً من ذلك، نستخدم دوال منشئة ببادئة New.

\n\n

مثال: إنشاء الإشعارات

\n

لنفترض أن نظامك يرسل إشعارات عبر البريد الإلكتروني والرسائل النصية القصيرة والإشعارات الفورية. بدلاً من توزيع منطق الاختيار عبر الكود، نستخدم مصنعًا.

\n\n
package notification\n\nimport "fmt"\n\ntype Notifier interface {\n    Send(message string) error\n}\n\ntype EmailNotifier struct{}\n\nfunc (e EmailNotifier) Send(message string) error {\n    fmt.Println("Sending email:", message)\n    return nil\n}\n\ntype SMSNotifier struct{}\n\nfunc (s SMSNotifier) Send(message string) error {\n    fmt.Println("Sending SMS:", message)\n    return nil\n}\n\nfunc NewNotifier(typ string) Notifier {\n    switch typ {\n    case "email":\n        return EmailNotifier{}\n    case "sms":\n        return SMSNotifier{}\n    default:\n        return nil\n    }\n}
\n\n

متى تستخدم: عندما يكون منطق الإنشاء معقدًا (يعتمد على النوع، الإصدار، الإعدادات).

\n

متى لا تستخدم: إذا كان لديك نوع واحد فقط من الكائنات. اكتب ببساطة MyStruct{} أو NewMyStruct() بدون مصنع.

\n\n

أنماط التصميم في Go: أمثلة إضافية

\n\n

بالإضافة إلى Singleton وFactory، هناك أنماط أخرى مفيدة في Go مثل Strategy وObserver وDecorator. كل منها يحل مشكلة محددة في تنظيم الكود وجعله أكثر مرونة.

\n\n

نمط Strategy يسمح بتغيير سلوك الخوارزمية في وقت التشغيل عن طريق تمرير استراتيجيات مختلفة. هذا مفيد عندما يكون لديك عدة طرق لأداء نفس المهمة، مثل طرق الدفع المختلفة أو خوارزميات الفرز.

\n\n

نمط Observer مفيد للأنظمة التي تحتاج إلى إخطار عدة مكونات عند حدوث حدث معين. في Go، يمكن تنفيذ ذلك باستخدام القنوات (channels) أو آليات الاستدعاء (callbacks).

\n\n

نمط Decorator يسمح بإضافة سلوكيات جديدة إلى الكائنات ديناميكيًا دون تعديل هيكلها الأصلي. هذا مفيد لإضافة وظائف مثل التسجيل أو التخزين المؤقت أو التحقق من الصحة.

\n\n

خلاصة: متى تستخدم أنماط التصميم في Go

\n\n

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

\n\n

في النهاية، الهدف هو كتابة كود نظيف وقابل للصيانة وفعال. أنماط التصميم هي مجرد أدوات في صندوق أدواتك - استخدمها بحكمة.

المدونات

توصيات الكتب