مبادئ SOLID في Go: المستوى المتقدم
\n\nSOLID هي خمسة مبادئ أساسية للتصميم الشيئي تساعد في إنشاء كود مرن وقابل للصيانة والتوسع. في سياق Go، وهي لغة ذات فلسفة فريدة تعتمد على التركيب بدلاً من الوراثة، يتطلب تطبيق SOLID نهجًا خاصًا. في هذه المقالة، سنشرح كل مبدأ على المستوى المتقدم، مع أمثلة حقيقية وأنماط مضادة شائعة في مجتمع Go.
\n\n1. مبدأ المسؤولية الواحدة (SRP) في Go
\n\nمبدأ المسؤولية الواحدة ينص على: يجب أن يكون للفئة (الهيكل) سبب واحد فقط للتغيير. في Go، غالبًا ما يُنتهك هذا المبدأ في الحزم المساعدة و"الكائنات الإلهية".
\n\nالنمط المضاد: هيكل بمسؤوليات متعددة
\n\ntype UserService struct {\n db *sql.DB\n cache *redis.Client\n logger *log.Logger\n}\n\nfunc (s *UserService) CreateUser(ctx context.Context, u User) error {\n // 1. التحقق\n // 2. تشفير كلمة المرور\n // 3. الحفظ في قاعدة البيانات\n // 4. إرسال البريد الإلكتروني\n // 5. التسجيل\n return nil\n}\n\nالحل: قسّم المسؤوليات إلى خدمات منفصلة واستخدم التركيب.
\n\ntype UserValidator interface {\n Validate(u User) error\n}\n\ntype PasswordHasher interface {\n Hash(password string) (string, error)\n}\n\ntype UserRepository interface {\n Save(ctx context.Context, u User) error\n}\n\ntype EmailSender interface {\n SendWelcomeEmail(ctx context.Context, email string) error\n}\n\ntype UserService struct {\n validator UserValidator\n hasher PasswordHasher\n repo UserRepository\n emailSender EmailSender\n logger Logger\n}\n\nأصبح لكل مكون الآن سبب واحد للتغيير، مما يبسط الاختبار والصيانة.
\n\n2. مبدأ الانفتاح/الإغلاق (OCP) عبر الواجهات
\n\nمبدأ الانفتاح/الإغلاق: يجب أن تكون الكيانات مفتوحة للتمديد ولكن مغلقة للتعديل. في Go، يتم تحقيق ذلك من خلال الواجهات وحقن التبعيات.
\n\nمثال سيء: الربط الصلب
\n\ntype PaymentProcessor struct {\n paypalAPI *PayPalAPI\n}\n\nfunc (p *PaymentProcessor) Process(amount float64) error {\n return p.paypalAPI.Charge(amount)\n}\n\nإذا أردت إضافة Stripe، فسيتعين عليك تغيير الكود.
\n\nالنهج الصحيح: واجهة + استراتيجية
\n\ntype PaymentGateway interface {\n Charge(amount float64) error\n Refund(transactionID string) error\n}\n\ntype PayPalGateway struct{ /* ... */ }\ntype StripeGateway struct{ /* ... */ }\n\nfunc (p *PayPalGateway) Charge(amount float64) error { /* ... */ }\nfunc (s *StripeGateway) Charge(amount float64) error { /* ... */ }\n\ntype PaymentService struct {\n gateway PaymentGateway\n}\n\nfunc NewPaymentService(gw PaymentGateway) *PaymentService {\n return &PaymentService{gateway: gw}\n}\n\nالآن يمكنك إضافة بوابات دفع جديدة دون تغيير PaymentService.
3. مبدأ استبدال ليسكوف (LSP) في سياق Go
\n\nمبدأ استبدال باربرا ليسكوف: يجب أن تكون الكائنات في البرنامج قابلة للاستبدال بنماذج من أنواعها الفرعية دون تغيير صحة البرنامج. في Go، حيث لا يوجد وراثة تقليدية، يظهر LSP من خلال عقود الواجهات.
\n\nانتهاك نموذجي: واجهة بطرق زائدة
\n\ntype Bird interface {\n Fly() error\n Swim() error\n}\n\ntype Penguin struct{}\n\nfunc (p *Penguin) Fly() error {\n return errors.New("penguins can't fly")\n}\n\nfunc (p *Penguin) Swim() error {\n return nil\n}\n\nهنا، لا يمكن لـ Penguin تنفيذ Fly() بشكل صحيح، مما ينتهك LSP.
الحل: فصل الواجهات
\n\ntype Flyer interface {\n Fly() error\n}\n\ntype Swimmer interface {\n S