أنماط التصميم في C++: متى ولماذا
\n\nهل سبق لك أن أعدت كتابة الكود للمرة العاشرة لأن وظيفة جديدة كسرت البنية القديمة؟ أو أمضيت ساعات في التصحيح محاولاً فهم من ولماذا غيّر متغيرًا عامًا؟ مألوف؟ إذن مرحبًا بك في عالم أنماط التصميم.
\n\nالأنماط ليست حبة سحرية. إنها حلول مجربة لمشاكل نموذجية. لن تجعل الكود الخاص بك مثاليًا، لكنها ستساعدك على التحدث بلغة واحدة مع المطورين الآخرين وتجنب الأخطاء التي وقع فيها آلاف المبرمجين قبلك.
\n\nفي هذه المقالة، سنحلل ثلاث مجموعات رئيسية من الأنماط في C++: الإنشائية والهيكلية والسلوكية. سنعرف متى يجب استخدامها ومتى يكون من الأفضل تجنبها. هيا بنا!
\n\nالأنماط الإنشائية: التحكم في إنشاء الكائنات
\n\nالأنماط الإنشائية مسؤولة عن إنشاء الكائنات بمرونة. فهي تخفي منطق التثبيت وتجعل النظام مستقلاً عن كيفية إنشاء مكوناته.
\n\nSingleton (المفرد)
\n\nكلاسيكي. يضمن وجود مثيل واحد فقط للفئة، ويوفر نقطة وصول عالمية إليه. يبدو مغريًا، لكن استخدمه بحكمة - غالبًا ما يصبح Singleton نمطًا معاديًا إذا تضخم إلى حجم "كائن الإله".
\n\nمتى تستخدم: مسجل (logger)، مدير التكوين، تجمع الخيوط (thread pool)، العمل مع الأجهزة.
\n\nمثال في C++ (آمن للخيوط، C++11 وما بعده):
\n\nclass Logger {\npublic:\n static Logger& getInstance() {\n static Logger instance; // يتم التهيئة مرة واحدة، حتى في تعدد الخيوط\n return instance;\n }\n\n void log(const std::string& msg) {\n std::cout << "[LOG]: " << msg << std::endl;\n }\n\n // منع النسخ والتعيين\n Logger(const Logger&) = delete;\n Logger& operator=(const Logger&) = delete;\n\nprivate:\n Logger() = default; // مُنشئ خاص\n ~Logger() = default;\n};\n\n// الاستخدام\nLogger::getInstance().log("تم تشغيل النظام");\n\nFactory Method (طريقة المصنع)
\n\nيحدد واجهة لإنشاء كائن، لكنه يسمح للفئات الفرعية بتغيير نوع الكائن الذي يتم إنشاؤه. مناسب تمامًا عندما لا تعرف مسبقًا أي فئة ستحتاجها.
\n\nمتى تستخدم: تحليل تنسيقات مختلفة (JSON، XML، YAML)، إنشاء عناصر واجهة مستخدم لأنظمة تشغيل مختلفة، العمل مع قواعد بيانات مختلفة.
\n\nمثال:
\n\n// منتج مجرد\nclass Document {\npublic:\n virtual void open() = 0;\n virtual ~Document() = default;\n};\n\n// منتجات محددة\nclass PDFDocument : public Document {\n void open() override { std::cout << "فتح PDF" << std::endl; }\n};\n\nclass WordDocument : public Document {\n void open() override { std::cout << "فتح Word" << std::endl; }\n};\n\n// المصنع\nclass Application {\npublic:\n virtual Document* createDocument() = 0; // طريقة المصنع\n virtual ~Application() = default;\n\n void newDocument() {\n Document* doc = createDocument();\n doc->open();\n delete doc; // في الكود الحقيقي - مؤشرات ذكية\n }\n};\n\nclass PDFApp : public Application {\n Document* createDocument() override { return new PDFDocument(); }\n};\n\n// الاستخدام\nPDFApp app;\napp.newDocument(); // فتح PDF\n\nالأنماط الهيكلية: تجميع المكونات
\n\nتساعد هذه الأنماط في تنظيم الفئات والكائنات في هياكل أكبر، مع الحفاظ على المرونة والكفاءة.
\n\nAdapter (المهايئ)
\n\nيسمح للكائنات ذات الواجهات غير المتوافقة بالعمل معًا. تخيل أن لديك مكتبة قديمة للعمل مع XML، ووحدة جديدة تنتظر JSON. المهايئ هو منقذك.
\n\nمتى تستخدم: