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

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

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

ابدأ الدورة

مقدمة: لماذا تعتبر مبادئ SOLID مهمة لمطور C++؟

\n\n

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

\n\n

في هذه المقالة، سنشرح كل مبدأ من مبادئ SOLID بأمثلة محددة بلغة C++. سترى كيف يؤدي انتهاك المبادئ إلى مشاكل، وكيفية إصلاحها. سنستخدم لغة C++ الحديثة (C++11/14/17/20) لتكون الأمثلة محدثة.

\n\n

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

\n\n

جوهر المبدأ

\n

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

\n\n

انتهاك SRP في C++

\n

لننظر إلى فصل ينتهك SRP: فهو يدير بيانات التقرير وتنسيقه في نفس الوقت.

\n\n
class Report {\npublic:\n    void generateReport(const std::vector<Data>& data) {\n        // منطق معالجة البيانات\n        m_data = data;\n    }\n    \n    void saveToFile(const std::string& filename) {\n        // منطق الحفظ في ملف\n        std::ofstream file(filename);\n        for (const auto& item : m_data) {\n            file << item.toString() << "\\n";\n        }\n    }\n    \n    void printToConsole() {\n        // منطق الطباعة على الشاشة\n        for (const auto& item : m_data) {\n            std::cout << item.toString() << std::endl;\n        }\n    }\n    \nprivate:\n    std::vector<Data> m_data;\n};
\n\n

المشكلة: إذا تغير تنسيق الإخراج، يجب تعديل فصل Report. إذا تغيرت طريقة تخزين البيانات - أيضاً. هذا ينتهك SRP.

\n\n

الإصلاح: فصل المسؤوليات

\n\n
// مسؤول فقط عن بيانات التقرير\nclass ReportData {\npublic:\n    void addData(const Data& item) {\n        m_data.push_back(item);\n    }\n    \n    const std::vector<Data>& getData() const {\n        return m_data;\n    }\n    \nprivate:\n    std::vector<Data> m_data;\n};\n\n// مسؤول فقط عن الحفظ\nclass FileSaver {\npublic:\n    void save(const std::string& filename, const ReportData& report) {\n        std::ofstream file(filename);\n        for (const auto& item : report.getData()) {\n            file << item.toString() << "\\n";\n        }\n    }\n};\n\n// مسؤول فقط عن الطباعة على الشاشة\nclass ConsolePrinter {\npublic:\n    void print(const ReportData& report) {\n        for (const auto& item : report.getData()) {\n            std::cout << item.toString() << std::endl;\n        }\n    }\n};
\n\n

الآن كل فصل لديه سبب واحد للتغيير، مما يجعل الكود أكثر مرونة وأسهل للاختبار.

\n\n

2. مبدأ مفتوح-مغلق (OCP)

\n\n

جوهر المبدأ

\n

يجب أن تكون الكيانات البرمجية (الفصول، الوحدات، الدوال) مفتوحة للتمديد ولكن مغلقة للتعديل. هذا يعني أنه يجب أن تكون قادراً على إضافة وظائف جديدة دون تغيير الكود الموجود.

\n\n

انتهاك OCP في C++

\n

تخيل نظام حساب الخصومات، حيث يتم استخدام if-else لكل نوع عميل.

\n\n
class DiscountCalculator {\npublic:\n    double calculateDiscount(const std::string& customerType, double amount) {\n        if (customerType == "regular") {\n            return amount * 0.1; // 10%\n        } else if (customerType == "vip") {\n            return amount * 0.2; // 20%\n        } else if (customerType == "premium") {\n            return amount * 0.3; // 30%\n        }\n                

المدونات

توصيات الكتب