مبادئ SOLID في Go: دليل متقدم مع أمثلة

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

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

ابدأ الدورة

مبادئ SOLID في Go: المستوى المتقدم

\n\n

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

\n\n

1. مبدأ المسؤولية الواحدة (SRP) في Go

\n\n

مبدأ المسؤولية الواحدة ينص على: يجب أن يكون للفئة (الهيكل) سبب واحد فقط للتغيير. في Go، غالبًا ما يُنتهك هذا المبدأ في الحزم المساعدة و"الكائنات الإلهية".

\n\n

النمط المضاد: هيكل بمسؤوليات متعددة

\n\n
type 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\n
type 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\n

2. مبدأ الانفتاح/الإغلاق (OCP) عبر الواجهات

\n\n

مبدأ الانفتاح/الإغلاق: يجب أن تكون الكيانات مفتوحة للتمديد ولكن مغلقة للتعديل. في Go، يتم تحقيق ذلك من خلال الواجهات وحقن التبعيات.

\n\n

مثال سيء: الربط الصلب

\n\n
type 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\n
type 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.

\n\n

3. مبدأ استبدال ليسكوف (LSP) في سياق Go

\n\n

مبدأ استبدال باربرا ليسكوف: يجب أن تكون الكائنات في البرنامج قابلة للاستبدال بنماذج من أنواعها الفرعية دون تغيير صحة البرنامج. في Go، حيث لا يوجد وراثة تقليدية، يظهر LSP من خلال عقود الواجهات.

\n\n

انتهاك نموذجي: واجهة بطرق زائدة

\n\n
type 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\n

الحل: فصل الواجهات

\n\n
type Flyer interface {\n    Fly() error\n}\n\ntype Swimmer interface {\n    S                

المدونات

توصيات الكتب