مبادئ SOLID في لغة C++: النظرية والتطبيق المتقدم

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

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

ابدأ الدورة

مقدمة: SOLID في سياق لغة C++

\n

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

\n

في هذه المقالة، سنشرح كل مبدأ بأمثلة عملية من كود C++ (17/20)، مع التركيز على تفاصيل التنفيذ: استخدام std::unique_ptr، std::function، CRTP، المدمرات الافتراضية، والتعابير الحديثة.

\n\n

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

\n

الصياغة: يجب أن يكون للفئة سبب واحد فقط للتغيير.

\n

عملياً، هذا يعني أن الفئة مسؤولة عن كيان تجاري واحد أو عملية واحدة. يؤدي انتهاك SRP إلى "تشابك" المسؤوليات، مما يعقد الاختبار والتعديل.

\n

مثال على الانتهاك

\n
class Report {\npublic:\n    void generate() { /* إنشاء البيانات */ }\n    void saveToFile(const std::string& path) { /* الكتابة إلى ملف */ }\n    void sendEmail(const std::string& to) { /* الإرسال */ }\n};
\n

هنا، Report مسؤول عن الإنشاء والحفظ والإرسال - ثلاثة أسباب للتغيير.

\n

إعادة الهيكلة مع SRP

\n
class Report {\npublic:\n    void generate() { /* إنشاء البيانات */ }\n};\n\nclass ReportSaver {\npublic:\n    void save(const Report& report, const std::string& path);\n};\n\nclass EmailSender {\npublic:\n    void send(const Report& report, const std::string& to);\n};
\n

الآن كل فئة لها مسؤولية واحدة. هذا يبسط الاختبار الوحدوي وإعادة الاستخدام.

\n\n

2. مبدأ الفتح/الإغلاق (OCP)

\n

الصياغة: يجب أن تكون الكيانات البرمجية مفتوحة للتمديد ولكن مغلقة للتعديل.

\n

في C++، يتم تحقيق ذلك غالباً من خلال تعدد الأشكال (الدوال الافتراضية) أو من خلال القوالب (CRTP). للمستوى المتقدم، سننظر في كلا التقنيتين.

\n

النهج متعدد الأشكال (الدوال الافتراضية)

\n
class Shape {\npublic:\n    virtual double area() const = 0;\n    virtual ~Shape() = default;\n};\n\nclass Circle : public Shape {\n    double r;\npublic:\n    Circle(double r) : r(r) {}\n    double area() const override { return 3.14 * r * r; }\n};\n\nclass Rectangle : public Shape {\n    double w, h;\npublic:\n    Rectangle(double w, double h) : w(w), h(h) {}\n    double area() const override { return w * h; }\n};
\n

إضافة شكل جديد (مثل Triangle) لا يتطلب تغيير الفئات الموجودة - فقط إنشاء فئة وريثة جديدة.

\n

النهج القالبي (CRTP - نمط القالب المتكرر الغريب)

\n
template<typename Derived>\nclass ShapeBase {\npublic:\n    double area() const { return static_cast<const Derived*>(this)->area_impl(); }\n};\n\nclass Circle : public ShapeBase<Circle> {\n    double r;\npublic:\n    Circle(double r) : r(r) {}\n    double area_impl() const { return 3.14 * r * r; }\n};
\n

CRTP يوفر تعدد أشكال ثابت دون تكاليف إضافية للاستدعاءات الافتراضية، وهو أمر بالغ الأهمية لـ C++ عالي الأداء.

\n\n

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

\n

الصياغة: يجب أن تكون كائنات الفئة الأساسية قابلة للاستبدال بكائنات الفئات المشتقة دون الإخلال بصحة البرنامج.

\n

الانتهاك الكلاسيكي لـ LSP في C++ هو إعادة تعريف دالة افتراضية مع إضعاف الشروط المسبقة أو تقوية الشروط اللاحقة. لننظر في مثال مع التسلسل الهرمي "مستطيل - مربع".

\n

النمط المضاد

\n
class Rectangle {\nprotected:\                

المدونات

توصيات الكتب